Servlet、Filter 生命周期
Servlet作为JavaEE必须掌握的内容,Struts2通过使用Filter的功能实现了一个MVC的框架。因此掌握这Servlet以及Filter的生命周期显得非常重要。
1. Servlet的生命周期
虽然通过使用IDE工具快速创建了Servlet,但是很多人没有弄明白这个东西到底是怎么什么时候实例化或者销毁的,丢开一阵子后很快就忘记服务器在后台到底做了什么。因此了解Servlet的生命周期非常必要。
Servlet是JavaEE标准下的一个接口,该接口抽象出了以下几个方法:
返回值 | 方法名称 | 作用 |
void |
init(ServletConfig config) |
初始化Servlet类实例 |
ServletConfig |
getServletConfig() |
获取含有初始化参数的Servlet配置文件 |
void |
service(ServletRequest req, ServletResponse res) |
调用Servlet的实例方法 |
void |
getServletInfo() |
获取Servlet的信息,包含版本、作者、版权等 |
void |
destroy() |
销毁Servlet类型内容 |
HttpServlet类基本实现了大部分Servlet接口的内容,目前使用IDE创建一个Servlet基本都是集成HttpServlet进行实现的。
回到Servlet生命周期。当用户发出了一个请求,tomcat这类服务器中间件是如何实例化用户编写的Servlet呢?这要从配置文件说起。
① 当用户在浏览器内输入请求,服务器中间件会根据web.xml去查找需要实例化的Servlet。如下:
1 <servlet> 2 <!-- ②根据servletName找到对应的Servlet,这个名称必须和mapping里面的名称一样 --> 3 <servlet-name>RequestServlet</servlet-name> 4 <!-- ③找到代码中需要实例化的具体对象位置,实例化对象 --> 5 <servlet-class>com.scl.controler.RequestServlet</servlet-class> 6 </servlet> 7 8 <servlet-mapping> 9 <servlet-name>RequestServlet</servlet-name> 10 <!--①中间件匹配url内的链接 --> 11 <url-pattern>/requestmap</url-pattern> 12 </servlet-mapping>
中间件通过反射查找到对应的类,进行实例化。
② 调用对应servlet类内的init方法。只运行一次init方法
③ 分析请求,servlet实例调用service方法。HttpServlet类把这个方法分成get和post两种请求方式处理相关逻辑。
④ 当服务器中间件被关闭或者web.xml被修改时,服务器中间件会调用servlet实例的destroy方法,对Servlet进行销毁。
1 package com.scl.controler; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class RequestServlet extends HttpServlet 11 { 12 private static final long serialVersionUID = 1L; 13 14 @Override 15 public void init() throws ServletException 16 { 17 System.out.println("servlet init!"); 18 } 19 20 @Override 21 public void destroy() 22 { 23 System.out.println("servlet destory!"); 24 } 25 26 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 27 { 28 System.out.println("do servlet method"); 29 } 30 31 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 32 { 33 doGet(request, response); 34 } 35 36 }
在浏览器访问http://localhost:8081/FilterProject/requestmap会得到如下结果
1 servlet init! 2 do servlet method
当关闭服务器中间件时会调用destroy方法,init方法只会调用一次。
2. Filter的生命周期
掌握Servlet以后,再接触基础的Filter类,会发现。这两块内容在web.xml里面的配置基本是一样的!先来看下Filter接口抽象了哪几个方法。
返回值 | 方法名称 | 作用 |
void |
init(FilterConfig filterConfig) |
初始化Filter实例 |
void |
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
调用Filter过滤内容 |
void |
destroy() |
销毁Filter实例 |
Filter 实例在启动web容器的时候就开始进行初始化,而Servlet实例则是在用户通过浏览器访问程序的时候调用。
① 当web容器(服务器中间件)启动的时候,建立Filter实例,初始化Filter接口。
② 当用户通过浏览器访问站点,Filter过滤用户请求
③ 进行Servlet服务处理
④ 当web容器退出时,servlet实例先销毁,再销毁filter实例
Filter配置:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 5 6 <filter> 7 <!-- ② --> 8 <filter-name>MyFilter</filter-name> 9 <!-- ③ --> 10 <filter-class>com.scl.filter.MyFilter</filter-class> 11 </filter> 12 13 <filter> 14 <filter-name>MyFilter2</filter-name> 15 <filter-class>com.scl.filter.MyFilter2</filter-class> 16 </filter> 17 <!-- 多个Filter,按照mapping里面的配置顺序进行过滤操作 --> 18 <filter-mapping> 19 <filter-name>MyFilter</filter-name> 20 <url-pattern>/*</url-pattern> 21 </filter-mapping> 22 <filter-mapping> 23 <filter-name>MyFilter2</filter-name> 24 <url-pattern>/*</url-pattern> 25 </filter-mapping> 26 27 <servlet> 28 <!-- ②根据servletName找到对应的Servlet,这个名称必须和mapping里面的名称一样 --> 29 <servlet-name>RequestServlet</servlet-name> 30 <!-- ③找到代码中需要实例化的具体对象位置,实例化对象 --> 31 <servlet-class>com.scl.controler.RequestServlet</servlet-class> 32 </servlet> 33 34 <servlet-mapping> 35 <servlet-name>RequestServlet</servlet-name> 36 <!--①中间件匹配url内的链接 --> 37 <url-pattern>/requestmap</url-pattern> 38 </servlet-mapping> 39 40 <welcome-file-list> 41 <welcome-file>index.html</welcome-file> 42 </welcome-file-list> 43 </web-app>
Filter 的使用必须通过类去实现Filter接口
1 package com.scl.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class MyFilter implements Filter 13 { 14 @Override 15 public void init(FilterConfig filterConfig) throws ServletException 16 { 17 System.out.println("myFilter init!"); 18 } 19 20 @Override 21 public void destroy() 22 { 23 System.out.println("myFilter destroy!"); 24 } 25 26 @Override 27 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 28 { 29 System.out.println("filter start"); 30 chain.doFilter(request, response); 31 System.out.println("filter end"); 32 } 33 34 }
1 package com.scl.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class MyFilter2 implements Filter 13 { 14 @Override 15 public void init(FilterConfig filterConfig) throws ServletException 16 { 17 System.out.println("myFilter2 init!"); 18 } 19 20 @Override 21 public void destroy() 22 { 23 System.out.println("myFilter2 destroy!"); 24 } 25 26 @Override 27 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 28 { 29 System.out.println("filter2 start"); 30 chain.doFilter(request, response); 31 System.out.println("filter2 end"); 32 } 33 34 }
访问http://localhost:8080/FilterProject/requestmap 会有以下结果
1 servlet init! 2 filter start 3 filter2 start 4 do servlet method 5 filter2 end 6 filter end
其中,在web容器运行的时候Filter就已经初始化了,并在控制台输出:
myFilter2 init!
myFilter init!
两个不同的Filter在运行的时候,按照filter-mapping中的顺序先后进行过滤。
关于url-mapping节点内容,有以下解析:
假设当前工程目录为:testmapping。
1 、url的精确匹配。
无论使用什么路径访问站点查找Servlet都是先以精确匹配开始。有 MapServlet 的url-pattern 设置为:/map,同时也有ServletB的url-pattern 设置为/* ; 假设用户输入http://localhost:8081/testmapping/map; 容器会找到MapServlet,并且只匹配MapServlet ,ServletB被放弃。(Filter 不一样,Filter是一个链式过滤,按照filter-mapping的顺序,都匹配上)。servlet会一层一层地查找,
2 、后缀名匹配。
url最后一段包含扩展,容器将会根据扩展选择合适的servlet。如 MapServlet 的url-pattern 设置为:"*.action";则访问http://localhost:8081/testmapping/a/a.action时,会匹配上。注意尽量不要直接使用".action"来指定,这样会报错。
3 、默认servlet。
当前两种方式没法匹配上的时候,如果是静态资源或者JSP文件,则走容器内部的default-servlet匹配。
以上为Servlet及Filter的生命周期总结,如有问题烦请指出纠正。