web.xml中servlet、filter、listener详解

servlet、filter、listener分别定义2个,代码如下:

public class GlobalServlet extends HttpServlet {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        logger.info("init");
    }
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("service");
        PrintWriter pw = resp.getWriter();
        pw.write("This is "+this.getClass().getSimpleName());
        pw.close();
    }
    
    @Override
    public void destroy() {
        logger.info("destroy");
    }
}

public class LoginServlet extends HttpServlet {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        logger.info("init");
    }
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("service");
        PrintWriter pw = resp.getWriter();
        pw.write("This is "+this.getClass().getSimpleName());
        pw.close();
    }
    
    @Override
    public void destroy() {
        logger.info("destroy");
    }
}

public class GlobalFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        logger.info("start doFilter");
        chain.doFilter(request, response);
        logger.info("end doFilter");
    }

    @Override
    public void destroy() {
        logger.info("destroy");
    }
}

public class LoginFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        logger.info("start doFilter");
        chain.doFilter(request, response);
        logger.info("end doFilter");
    }

    @Override
    public void destroy() {
        logger.info("destroy");
    }
}

public class ContextListener1 implements ServletContextListener {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("contextDestroyed");
    }
}

public class ContextListener2 implements ServletContextListener {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("contextDestroyed");
    }
}

Login开头的servlet和filter匹配单一路径:/login,Global开头的servlet和filter匹配所有路径:/,匹配所有路径的servlet和filter以及ContextListener1放上面,匹配单一路径的servlet和filter以及ContextListener2放下面,web.xml配置如下:

<servlet>
    <servlet-name>GlobalServlet</servlet-name>
    <servlet-class>com.test.GlobalServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GlobalServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.test.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>GlobalFilter</filter-name>
    <filter-class>com.test.GlobalFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>GlobalFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>LoginFilter</filter-name>
    <filter-class>com.test.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoginFilter</filter-name>
    <url-pattern>/login</url-pattern>
</filter-mapping>

<listener>
    <listener-class>com.test.ContextListener1</listener-class>
</listener>
<listener>
    <listener-class>com.test.ContextListener2</listener-class>
</listener>

启动Tomcat时,日志如下:

ContextListener1 - contextInitialized
ContextListener2 - contextInitialized
GlobalFilter - init
LoginFilter - init

第1次访问/login时,日志如下:

LoginServlet - init
GlobalFilter - start doFilter
LoginFilter - start doFilter
LoginServlet - service
LoginFilter - end doFilter
GlobalFilter - end doFilter

第2次访问/login时,日志如下:

GlobalFilter - start doFilter
LoginFilter - start doFilter
LoginServlet - service
LoginFilter - end doFilter
GlobalFilter - end doFilter

第1次访问/test时,日志如下:

GlobalServlet - init
GlobalFilter - start doFilter
GlobalServlet - service
GlobalFilter - end doFilter

第2次访问/test时,日志如下:

GlobalFilter - start doFilter
GlobalServlet - service
GlobalFilter - end doFilter

关闭Tomcat时,日志如下:

GlobalServlet - destroy
LoginServlet - destroy
GlobalFilter - destroy
LoginFilter - destroy
ContextListener2 - contextDestroyed
ContextListener1 - contextDestroyed

匹配单一路径的servlet和filter以及ContextListener2放上面,匹配所有路径的servlet和filter以及ContextListener1放下面,web.xml配置如下:

<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.test.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>GlobalServlet</servlet-name>
    <servlet-class>com.test.GlobalServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GlobalServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>LoginFilter</filter-name>
    <filter-class>com.test.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoginFilter</filter-name>
    <url-pattern>/login</url-pattern>
</filter-mapping>

<filter>
    <filter-name>GlobalFilter</filter-name>
    <filter-class>com.test.GlobalFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>GlobalFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
    <listener-class>com.test.ContextListener2</listener-class>
</listener>
<listener>
    <listener-class>com.test.ContextListener1</listener-class>
</listener>

启动Tomcat时,日志如下:

ContextListener2 - contextInitialized
ContextListener1 - contextInitialized
GlobalFilter - init
LoginFilter - init

第1次访问/login时,日志如下:

LoginServlet - init
LoginFilter - start doFilter
GlobalFilter - start doFilter
LoginServlet - service
GlobalFilter - end doFilter
LoginFilter - end doFilter

第2次访问/login时,日志如下:

LoginFilter - start doFilter
GlobalFilter - start doFilter
LoginServlet - service
GlobalFilter - end doFilter
LoginFilter - end doFilter

第1次访问/test时,日志如下:

GlobalServlet - init
GlobalFilter - start doFilter
GlobalServlet - service
GlobalFilter - end doFilter

第2次访问/test时,日志如下:

GlobalFilter - start doFilter
GlobalServlet - service
GlobalFilter - end doFilter

关闭Tomcat时,日志如下:

GlobalServlet - destroy
LoginServlet - destroy
GlobalFilter - destroy
LoginFilter - destroy
ContextListener1 - contextDestroyed
ContextListener2 - contextDestroyed

servlet、filter、listener只会初始化和销毁一次,初始化顺序为:Listener(在Tomcat启动时初始化,多个按web.xml中listener-class的顺序排序)->Filter(在Tomcat启动时初始化,多个按filter-name在HashMap中的散列顺序排序)->Servlet(如果没有设置load-on-startup或设置的load-on-startup<0,在Servlet第一次请求时初始化,如果设置了load-on-startup且值>=0,在Tomcat启动时初始化,多个按load-on-startup从小到大排序,如果load-on-startup的值相同则按servlet-name在HashMap中的散列顺序排序),销毁顺序为:Servlet(在Tomcat关闭时销毁,多个按servlet-name在HashMap中的散列顺序排序,如果此Servlet没有初始化则不销毁)->Filter(在Tomcat关闭时销毁,多个按filter-name在HashMap中的散列顺序排序)->Listener(在Tomcat关闭时销毁,多个按web.xml中listener-class的倒序排序)。

在请求过程中,如果最优先匹配的那个Servlet没有初始化,则先初始化此Servlet,然后调用所有匹配的filter(按filter-mapping的顺序排序)的doFilter(),再调用最优先匹配的那个servlet的service()。在filter中的doFilter()调用chain.doFilter(request, response);表示调用下一个filter,如果这是最后一个filter,表示调用请求资源(如:servlet、jsp、html、js、css、image等),如果没有调用chain.doFilter(request, response);,则后续的filter和请求资源将不会调用,filter和servlet为链的关系,先进后出。

servlet的url-pattern匹配顺序为:精确匹配->路径匹配(必须是[/...]/,长路径优先)->后辍匹配(必须是.xxx)->缺省匹配(/),容器按匹配顺序匹配到一个servlet后就停止匹配并立即返回,/xxx/.jsp是非法的,因为容器没办法判断这是路径匹配还是后辍匹配,容器启动时会报错。servlet的url-pattern映射为/和/的区别如下:

  1. /表示这是缺省Servlet,属于缺省匹配,所有已定义的Servlet都没有匹配上,才会使用缺省Servlet,优先级最低。
  2. /属于路径匹配,优先级比较高,比后辍匹配的优先级还高,比如:/比*.jsp优先级高。

filter的url-pattern匹配规则跟servlet类似,但是filter的匹配顺序按filter-mapping的顺序排序,并且容器匹配到filter后还会继续匹配,直到匹配出所有符合条件的filter,filter没有缺省匹配这一说法,filter的url-pattern如果映射为/,会当做精确匹配来使用。

Tomcat中内置了两个servlet,servlet-name为default的DefaultServlet用来处理静态资源(比如:html、js、css、image)和展示目录列表,servlet-name为jsp的JspServlet用来编译和执行jsp文件。default的url-pattern映射为/表示这是缺省Servlet。jsp的url-pattern映射为.jsp和.jspx表示所有jsp和jspx后辍的请求将由此Servlet处理。如果应用程序的servlet的url-pattern映射为/将会覆盖Tomcat内置的DefaultServlet,此应用程序的servlet将成为缺省Servlet。如果应用程序的servlet的url-pattern映射为.jsp或.jspx将会覆盖Tomcat内置的JspServlet,所有jsp或jspx后辍的请求将由此应用程序的servlet处理。

标签: web.xml中servlet、filter、listener详解

添加新评论