zoukankan      html  css  js  c++  java
  • Filter与Servlet的区别和联系

    Filter的执行顺序与实例

    Filter介绍 

    Filter可认为是Servlet的一种“变种”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

    Filter有如下几个用处。

    • 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
    • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
    • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
    • 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

    Filter有如下几个种类。

    • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
    • 日志Filter:详细记录某些特殊的用户请求。
    • 负责解码的Filter:包括对非标准编码的请求解码。
    • 能改变XML内容的XSLT Filter等。
    • Filter可负责拦截多个请求或响应;一个请求或响应也可被多个请求拦截。

    创建一个Filter只需两个步骤:

    • 建Filter处理类;
    • web.xml文件中配置Filter。

    下面先介绍一个简单的记录日志的Filter,这个Filter负责拦截所有的用户请求,并将请求的信息记录在日志中。

    复制代码
    代码
    public class LogFilter implements Filter 
    {
    //FilterConfig可用于访问Filter的配置信息
    private FilterConfig config;
    //实现初始化方法
    public void init(FilterConfig config)
    {
    this.config = config; 
    }
    //实现销毁方法
    public void destroy()
    {
    this.config = null; 
    }
    //执行过滤的核心方法
    public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException
    {
    //---------下面代码用于对用户请求执行预处理---------
    //获取ServletContext对象,用于记录日志
    ServletContext context = this.config.getServletContext(); 
    long before = System.currentTimeMillis();
    System.out.println("开始过滤...");
    //将请求转换成HttpServletRequest请求
    HttpServletRequest hrequest = (HttpServletRequest)request;
    //记录日志
    context.log("Filter已经截获到用户的请求地址: " + hrequest.getServletPath());
    //Filter只是链式处理,请求依然放行到目的地址
    chain.doFilter(request, response); 
    //---------下面代码用于对服务器响应执行后处理---------
    long after = System.currentTimeMillis();
    //记录日志
    context.log("过滤结束");
    //再次记录日志
    context.log("请求被定位到" + hrequest.getRequestURI() + "所花的时间为: " + (after - before)); 
    }
    }
    复制代码

    上面程序实现了doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

    在上面的请求Filter中,仅在日志中记录请求的URL,对所有的请求都执行chain.doFilter (request,reponse)方法,当Filter对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter中根据用户请求的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request,reponse)方法。

    代码
    复制代码
    ==================
    FirstFilter.java   
    ==================   
    package com.test.filter;   
      
    import java.io.IOException;   
      
    import javax.servlet.Filter;   
    import javax.servlet.FilterChain;   
    import javax.servlet.FilterConfig;   
    import javax.servlet.ServletException;   
    import javax.servlet.ServletRequest;   
    import javax.servlet.ServletResponse;   
      
    public class FirstFilter implements Filter {   
      
        @Override  
        public void destroy() {   
      
        }   
      
        @Override  
        public void doFilter(ServletRequest request, ServletResponse response,   
                FilterChain chain) throws IOException, ServletException {   
            System.out.println("before invoke firstFilter's chain.doFilter() ..");   
            chain.doFilter(request, response);   
            System.out.println("after invoke firstFilter's chain.doFilter() ..");   
        }   
      
        @Override  
        public void init(FilterConfig arg0) throws ServletException {   
            System.out.println("firstFilter init()..."); 
        } 
    }     
      
    ============   
    SecondFilter.java   
    =============  
      
    package com.test.filter;   
      
    import java.io.IOException;   
      
    import javax.servlet.Filter;   
    import javax.servlet.FilterChain;   
    import javax.servlet.FilterConfig;   
    import javax.servlet.ServletException;   
    import javax.servlet.ServletRequest;   
    import javax.servlet.ServletResponse;   
      
    public class SecondFilter implements Filter {   
      
        @Override  
        public void destroy() {   
      
        }   
      
        @Override  
        public void doFilter(ServletRequest request, ServletResponse response,   
                FilterChain chain) throws IOException, ServletException {   
            System.out.println("before invoke secondFilter's chain.doFilter() ..");   
            chain.doFilter(request, response);   
            System.out.println("after invoke secondFilter's chain.doFilter() ..");   
        }   
      
        @Override  
        public void init(FilterConfig filterConfig) throws ServletException {   
            System.out.println("secondFilter init()...");   
        }
    }   
    ==========  
    FirstServlet.java   
    ==========  
    package com.test.servlet;   
      
    import java.io.IOException;   
      
    import javax.servlet.ServletException;   
    import javax.servlet.http.HttpServlet;   
    import javax.servlet.http.HttpServletRequest;   
    import javax.servlet.http.HttpServletResponse;   
      
    public class FirstServlet extends HttpServlet {   
      
        @Override  
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)   
                throws ServletException, IOException {   
            System.out.println("servlet doGet be invoked...");   
            req.getRequestDispatcher("test.jsp").forward(req, resp);   
        }   
      
        @Override  
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)   
                throws ServletException, IOException {   
            // TODO Auto-generated method stub   
            doGet(req, resp);   
        } 
    复制代码

    web.xml  

    复制代码
    代码
      
    <?xml version="1.0" encoding="UTF-8"?>   
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">   
        <welcome-file-list>   
            <welcome-file>index.jsp</welcome-file>   
        </welcome-file-list>   
        <filter>   
            <filter-name>firstFilter</filter-name>   
            <filter-class>com.test.filter.FirstFilter</filter-class>   
        </filter>   
        <filter>   
            <filter-name>secondFilter</filter-name>   
            <filter-class>com.test.filter.SecondFilter</filter-class>   
        </filter>   
        <filter-mapping>   
            <filter-name>secondFilter</filter-name>   
            <url-pattern>/*</url-pattern>   
        </filter-mapping>   
        <filter-mapping>   
            <filter-name>firstFilter</filter-name>   
            <url-pattern>/*</url-pattern>   
        </filter-mapping>   
      
        <servlet>   
            <servlet-name>firstServlet</servlet-name>   
            <servlet-class>com.alimama.servlet.FirstServlet</servlet-class>   
        </servlet>   
        <servlet-mapping>   
            <servlet-name>firstServlet</servlet-name>   
            <url-pattern>/firstServlet</url-pattern>   
        </servlet-mapping>   
    </web-app>   
    复制代码

     然后发布,发现打印的日志如下:

    。。。

    firstFilter init()...
    secondFilter init()...

    。。。
    信息: Server startup in 3665 ms

    这里过滤器初始化好了。

    当我们访问我们的 应用:http://127.0.0.1:8080/appName

    发现打印日记如下:

    before invoke secondFilter's chain.doFilter() ..
    before invoke firstFilter's chain.doFilter() ..
    after invoke firstFilter's chain.doFilter() ..
    after invoke secondFilter's chain.doFilter() ..

    当我们将web.xml中filter的位置进行调整后(注意filter-mapping的顺序):

    复制代码
    代码
    <?xml version="1.0" encoding="UTF-8"?>  
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
        <welcome-file-list>  
            <welcome-file>index.jsp</welcome-file>  
        </welcome-file-list>  
        <filter>  
            <filter-name>firstFilter</filter-name>  
            <filter-class>com.test.filter.FirstFilter</filter-class>  
        </filter>  
        <filter>  
            <filter-name>secondFilter</filter-name>  
            <filter-class>com.test.filter.SecondFilter</filter-class>  
        </filter>  
                <SPAN style="COLOR: #ff0000"> <filter-mapping>  
            <filter-name>firstFilter</filter-name>  
            <url-pattern>/*</url-pattern>  
        </filter-mapping>  
      
        <filter-mapping>  
            <filter-name>secondFilter</filter-name>  
            <url-pattern>/*</url-pattern>  
        </filter-mapping></SPAN>  
           
        <servlet>  
            <servlet-name>firstServlet</servlet-name>  
            <servlet-class>com.alimama.servlet.FirstServlet</servlet-class>  
        </servlet>  
        <servlet-mapping>  
            <servlet-name>firstServlet</servlet-name>  
            <url-pattern>/firstServlet</url-pattern>  
        </servlet-mapping>  
    </web-app>  
    复制代码

    然后在启动应用,会看到打印:

    before invoke firstFilter's chain.doFilter() ..

    before invoke secondFilter's chain.doFilter() ..
    after invoke secondFilter's chain.doFilter() ..

    after invoke firstFilter's chain.doFilter() ..

    下面是一个实例:

    代码

    上面Filter的doFilter方法里3行斜体字代码用于获取Filter的配置参数,而程序中粗体字代码则是此Filter的核心,①号代码按配置参数设置了request编码所用的字符集,接下来的粗体字代码判断session范围内是否有user属性——没有该属性即认为没有登录,如果既没有登录,而且请求地址也不是登录页和处理登录页,系统直接跳转到登录页面。

    在web.xml文件中配置该Filter,使用init-param元素为该Filter配置参数,init-param可接受如下两个子元素:

    param-name:指定参数名。

    param-value:指定参数值。

    该Filter的配置片段如下:

    代码

    上面配置片段中粗体字代码为该Filter指定了3个配置参数,指定loginPage为/login.jsp,proLogin为/proLogin.jsp,这表明:如果没有登录该应用,普通用户只能访问/login.jsp和/proLogin.jsp页面。只有当用户登录该应用后才可自由访问其他页面。

    实际上Filter和Servlet极其相似,区别只是Filter不能直接对用户生成响应。实际上Filter里doFilter()方法里的代码就是从多个Servlet的service()方法里抽取的通用代码,通过使用Filter可以实现更好的复用。

    web.xml 中的listener、 filter、servlet 加载顺序及其详解

    在项目中总会遇到一些关于加载的优先级问题,刚刚就遇到了一个问题,由于项目中使用了quartz任务调度,quartz在web.xml中是使用listener进行监听的,使得在tomcat启动的时候能马上检查数据库查看那些任务未被按时执行,而数据库的配置信息在是在web.xml中使用servlet配置的,导致tomcat启动后在执行quartz任务时报空指针,原因就是servlet中的数据库连接信息未被加载。网上查询了下web.xml中配置的加载优先级:

            首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。最终得出的结论是:listener -> filter -> servlet

            同时还存在着这样一种配置节:context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上 context-param 配置节可写在任意位置,因此真正的加载顺序为:context-param -> listener -> filter -> servlet

            对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例,web.xml 中当然可以定义多个 filter,与 filter 相关的一个配置节是 filter-mapping,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言,filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。

            servlet 同 filter 类似 ,此处不再赘述。

           由此,可以看出,web.xml 的加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
  • 相关阅读:
    leetcode 48. Rotate Image
    leetcode 203. Remove Linked List Elements 、83. Remove Duplicates from Sorted List 、82. Remove Duplicates from Sorted List II(剑指offer57 删除链表中重复的结点) 、26/80. Remove Duplicates from Sorted ArrayI、II
    leetcode 263. Ugly Number 、264. Ugly Number II 、313. Super Ugly Number 、204. Count Primes
    leetcode 58. Length of Last Word
    安卓操作的一些问题解决
    leetcode 378. Kth Smallest Element in a Sorted Matrix
    android studio Gradle Build速度加快方法
    禁用gridview,listview回弹或下拉悬停
    Android Studio找不到FragmentActivity类
    安卓获取ListView、GridView等滚动的距离(高度)
  • 原文地址:https://www.cnblogs.com/prctice/p/5293976.html
Copyright © 2011-2022 走看看