一. 前言
在写SSM项目的过程中,遇到了一些小问题,不知道如何解决。其实对于框架的很多问题,很多时候都是因为框架的封装性,然后自身没有理解框架的内部逻辑而导致的。于是这次想花点时间好好读一下Servlet的源码,这样可以更好地理解JavaWeb底层,遇到问题也能更加地游刃有余。当然此次阅读源码只是比较浅显的,先看官方文档,然后在有目的性地读一部分源码来感受文档的内容。如果要认真看那肯定要看很久,目前还没这个时间。
首先是Servlet API包含了两个软件包,javax.servlet和javax.servlet.http,为什么会有两个呢,因为早先设计该规范的人认为Servlet是一种服务模型,不一定要依赖于某种网络协议之上,于是就抽象出了一个javax.servlet,同时再提供一个基于HTTP协议上的接口拓展。但目前看来,似乎还没有发现有其他协议上实现的Servlet技术。
二. HTTPServlet抽象类
)](https://my-blog-1253941628.cos.ap-guangzhou.myqcloud.com/2020-02-14/1.jpg)
官方解析:
Provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site.A subclass of HttpServlet must override at leat one method usually one of these: doGet,doPost,doPut,doDelete.
提供一个抽象类,使得它的子类可以更方便地创建一个标准HTTP Servlet。子类必须至少实现以下方法的其中一个:doGet,doPost,doPut,doDelete
标准Servlet接口有一个service方法,即service的标准服务方法。但是service方法没必要override,因为HTTPServlet已经把service方法写好了,把标准的HTTP请求通过它们的HTTP request类型(即请求的doXXX),分别转发到具体的处理方法中。比如类型是doGet,就转发到doGet方法中。
HttpServlet里的service方法源码:
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) |
可以看到是通过Java反射的机制,获取了HttpServletRequest的方法,返回了一个method的字符串,根据method来判定到底是属于哪一个doXXX,再分别调用doXXX方法。这里我钻了一下牛角尖,HttpServletRequest里有多个方法,getMethod方法的内部逻辑是如何返回的呢。HttpServletRequest只是一个接口,寻找了很久只找到了一个实现类HttpServletRequestWrapper,然而还是没有具体的getMethod实现。最后知道了这个HttpServletRequest是由J2EE的容器来实现的,比如Tomcat,webLogic。看来我们是不能得知getMethod的具体逻辑了,只需要知道它可以返回HTTP报文头的method是什么就行。
然后如果是METHOD_XXX方法,就调用doXXX方法。好几个方法都没有特别,直接调用。但HEAD方法和GET方法多了一个逻辑,用于验证文件是否已经被修改。HEAD和GET都是获取服务器上的资源,但HEAD不需要返回消息体,只请求返回报文头,空的消息体,也就是说,它可以用于检测资源是否有效,是否存在,网站是否有篡改等。当需要这类操作的时候,就不需要用GET了,用HEAD即可。如果本地有缓存文件,和要GET的文件是相同的,没有修改过的,那么此时就会返回状态码304,表示文件没有被修改。虽然返回304的时候已经做了一次数据库查询,但可以避免接下来更多的数据库查询。对于HEAD方法,只返回HTTP报文头,更是降低了带宽的消耗。
最终到达了doXXX的方法,以GET方法为例,代码如下:
1 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) |
这里有个特别的地方是,虽然doXXX方法有具体的实现,但它是无论如何都会返回错误的,即sendError。这是避免了具体子类直接调用HttpServlet的doXXX方法。因此子类必须重写至少1个doXXX方法,然后再通过HTTP的method判断要调用哪个doXXX方法,进而调用子类重写的doXXX方法。
三. Servlet, ServiceConfig接口
一个Java Servlet具有生命周期,这个生命周期定义了一个Servlet如何被载入并被初始化,如何接受请求并对请求的响应,如何从服务中被清除等等。Servlet的生命周期被javax.servlet.Servlet这个接口所定义。
所有的Java Servlet都会直接或间接地实现javax.servlet.Servlet接口,这样它才能在一个servlet引擎中运行。servlet引擎是web服务器按照Java Servlet API定制的拓展。Servlet引擎提供网络服务,能够理解MIME请求,并提供一个运行Servlet的容器。
只有实现了Servlet接口的才是Servlet,然后才包含Servlet的生命周期。当然,继承了HttpServlet,就是继承了GenericServlet,就是实现了Servlet接口。
ServletConfig就是用于获取Servlet的配置,比如初始化参数,参数名等等,还可以获取该Servlet的ServletContext对象。
四. ServletContext接口
Servlet上下文,服务器会为工程建立一个全局唯一的ServletContext对象,工程内部所有的Servlet都共享这个对象,可以用于读取全局配置参数,搜索资源文件等等。
五. ServletRequest接口等
ServletRequest接口,请求时产生的对象,ServletResponse接口,响应时产生的对象。
SingleThreadModel接口,使得Servlet成为单线程。(只接受1个请求)
GenericServlet类,实现了Servlet接口,使得编写Servlet更方便。
ServletInputStream,ServletOutputStream抽象类,继承InputStream,OutputStream
ServletException,UnavailableException类,没什么好说的。
六. Filter接口
用于对request,response等任务进行过滤,一般是用doFilter方法来过滤,可以通过FilterConfig的对象来获取Filter的初始化参数。包含了destroy,init,doFilter方法。
生命周期:当有Servlet的service方法要被执行时,被init,直到所有的service方法已经执行完毕doFilter,或者过了一定的时间之后,就会执行destroy方法。
对具体实现类的基本要求:
①要对request进行验证检查。
②对request和response可以选择自定义实现类去包装
③当有多个过滤方法的时候,需要用到过滤链FilterChain,并且在当前的Filter没执行完之前,当前的数据都不会被通过。执行完之后会继续执行chain里的下一个filter
④在过滤链调用下一个filter的时候,就要直接设置当前的response header(避免窜数据)
FilterChain的多个filter执行顺序在web.xml的filter-mapping里控制
FilterConfig,对Filter的初始化配置参数等等。
七. RequestDispatcher接口
直译就是请求分发器。具备两个方法,forward和include。
定义了一个对象,从客户端接受请求之后,需要将请求发给服务器。有的时候需要传递给另一个资源去响应。比如用户请求资源A,A可以在接受请求后,在A上面做一些准备工作,再从A转发转发大都B,由B去响应例子。
第一个request就是A的准备工作,第二个就是转发到B,由B去响应。
include不是转发,是包含某个资源,比如Servlet,JSP,HTML页面
八. HttpServletRequest,HttpServletResponse接口
继承ServletRequest,ServletResponse接口,用于处理一个HTTP格式的请求/响应
主要方法,setAttribute,getAttribute(继承得来的),getSession等等。Response同理。
九. HTTPSession接口
创建一个HTTP会话。
方法主要是获取Session的参数(ID,creationTime,最长存在时间等等),以及setAttribute。。
HttpSessionBindingListener接口。HTTPSession监听器,添加到Session中,监听Session,此处是监听binding,绑定事件。
还有其他的Session的Listener,比如Attribute,Activation监听器。(也就是HttpSessionAttributeListener,HttpSessionActivationListener)
HttpSessionBindingEvent类,实现了HttpSessionBindingListener接口。只多了两个成员属性。
HttpUtils类,收集HTTP Servlet使用的静态的有效方法。