servlet3.1英文原版下载:https://download.oracle.com/otndocs/jcp/servlet-3_1-fr-spec/index.html
持续更新,未完
目录
第2章 servlet 接口... 1
2.1 谁来处理每个请求... 1
2.1.1 处理HTTP 请求的方法... 1
2.3 servlet 的生命周期... 1
2.3.1 servlet的加载和实例化... 2
2.3.2 servlet的初始化... 3
2.3.3 请求的处理... 4
每个 servlet都拥有一套非常完善的生命周期管理机制;
这套机制决定了每个 servlet 如何被加载,被实例化,被初始化,如何处理客户端的请求,如何被 service 方法所调用。这套机制反应在API里的话,对应着如下3个方法:init, service, 和 destroy,
这3个方法均来自于javax.servlet.Servlet 这个接口, 所有的 servlet 都要直接或间接的实现这个接口,其中,间接的实现这个接口,可以通过继承 GenericServlet 和 HttpServlet这2个抽象类来实现,在 TomCat8 中,HttpServlet继承了GenericServlet,而GenericServlet实现了 Servlet接口。
其中,GenericServlet 的全称是
javax.servlet.GenericServlet
HttpServlet 的全称是 javax.servlet.http.HttpServlet
这2个抽象类都位于TomCat 8 的 /lib/servlet-api.jar 中。
Servlet 容器负责 所有servlet 的加载和实例化。什么时候servlet会完成加载并且实例化呢?有2种情况:(1)servlet容器已经启动了;(2)servlet容器未启动,直到客户端的请求需要一个servlet去处理时,启动servlet 容器。
不管是哪种情况,只要 servlet容器启动了,所有该被加载的servlet都会被servlet容器所加载。所使用的类加载机制和那些普通的java类加载机制是一样的。可能通过本地class文件,或者远程class文件以及其他网络途径加载。
在加载完所有servlet类之后,servlet容器就会把它们都一个一个实例化,以备后用。
当servlet对象已经被实例化之后,servlet容器必须再将其初始化,servlet对象才可以处理客户端请求。那么,在初始化的过程中,都能干哪些事情呢:servlet可以加载持久化配置信息,可以初始化一些系统开销比较大的资源(比如JDBC的连接),以及一些只需加载一次的资源。Servlet容器会让每个servlet实例调用servlet接口的init方法来实现初始化,同时,还会让servlet实例实现ServletConfig接口来以名-值对的形式获取web应用的配置信息中的初始化参数,还可以获取实现了ServletContext接口的对象,这个对象描述了servlet的运行时环境。ServletContext见第4章。
在tomcat8中,GenericServlet implements Servlet, ServletConfig
2.3.2.1 初始化中的异常
Servlet在初始化过程中会出现2个异常:UnavailableException和ServletException.一旦出现了这2个异常,这个servlet已经不能使用了并且会被servlet容器所释放。注意,当servlet初始化失败时,destroy方法并不会被调用。
当servlet初始化失败时,servlet容器会重新实例化和初始化一个新的servlet对象。需要说明的是,当UnavailableException异常规定了这个servlet不可用时间时,servlet容器会等到这段时间过去后,才会着手重新实例化和初始化这个新的servlet对象。
2.3.2.2 关于工具的思考
当一个工具被加载并且作用于整个web应用时,静态初始化方法的调用不等于init方法的调用。在init方法未被调用之前,我们不能认为这个servlet是可用的。比如,当servlet容器只调用了这个servlet静态初始化方法时,那么这个servlet不能建立数据库连接和企业级javabean容器的连接
当一个servlet被正确的初始化之后,servlet容器就可以使用这个servlet来处理客户端的请求了(servlet容器调用service方法)。请求被servlet容器封装成了javax.servlet.ServletRequest 对象。响应内容也被封装成了javax.servlet. ServletResponse.对象。这2个对象都作为参数传给了service方法。
如果是HTTP请求,则ServletRequest 对象被servlet容器转换为javax.servlet.http.HttpServletRequest对象,ServletResponse 对象被转换为javax.servlet.http.HttpServletResponse对象。
一个servlet实例就绪之后,只要没有相应的请求过来,那么他自己是不会处理任何事情的。
2.3.3.1 多线程问题
Servlet容器在遇到并发请求时,也是通过调用service方法来处理的。所以,servlet开发者必须做好充足的准备。
尽管servlet通过实现SingleThreadModel接口来解决多线程问题的这种做法不被推荐,但也算是一种替代方案。只要一个servlet实现了SingleThreadModel接口,则servlet容器就会保证在同一时刻,一个请求线程只对应一个service方法。Servlet容器通过序列化一个servlet的请求或者维持一个servlet实例池来保证客户端请求的线程安全。
如果在一个分布式的web应用中,servlet容器会在每一个jvm中维持一个servlet实例池的。
如果servlet没有实现SingleThreadModel接口,但是service方法或者在service方法中调用的方法(比如doGet,doPost等等)声明了synchronized标志,那么servlet容器就不能使用servlet实例池了,而是必须序列化这个请求。因此,强烈建议在开发过程中不要对service方法或者在service方法中调用的方法(比如doGet,doPost等等)声明synchronized标志,因为这样做会对程序造成不利的影响。
2.3.3.2 请求处理中的异常
当一个servlet处理请求时,不外乎会抛出2种类型的异常:ServletException和UnavailableException。当抛出ServletException类型的异常意味着servlet在处理请求的过程中出现了错误,当这种类型的异常发生时,servlet容器应当采取适当的措施去清理这个请求;
当抛出UnavailableException类型的异常意味着,无论何时(分为暂时性和永久性),这个servlet都无法处理这个请求。
如果UnavailableException异常信息里面表明是永久性不可用,那么servlet容器会让这个servlet停止服务,并且调用其destroy方法,接着释放servlet实例资源。(这里对比一下 .2.3.2.1 初始化中的异常 中对destroy方法的说明)。最后返回状态码404;
如果UnavailableException异常信息里面表明是暂时性不可用,servlet容器会在一段时间内不让这个servlet处理任何请求,并且返回错误状态码503.和一个Retry-After响应头,这个响应头代表这段不可访问的时间会持续多久。
不过,servlet容器会忽略永久性和暂时性的区别。只要servlet抛出UnavailableException类型的异常,都视为永久性不可用,处理方法按照永久性不可用的来。
2.3.3.3 异步处理(待续,请继续关注)