zoukankan      html  css  js  c++  java
  • 设计模式(13)--Chain of Responsibility(责任链模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入!

    1.模式定义:

      责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

    2.模式特点:

      责任链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用责任链模式,例如在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用责任链模式可以较好地解决此类问题。
      另外:责任链模式其实就是一个灵活版的if…else…语句,它就是将这些判定条件的语句放到了各个处理类中,这样做的优点是比较灵活了,但同样也带来了风险,比如设置处理类前后关系时,一定要特别仔细,搞对处理类前后逻辑的条件判断关系,并且注意不要在链中出现循环引用的问题。
     责任链模式的要点主要是:
      (1)有多个对象共同对一个任务进行处理。
      (2)这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。
      (3)一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。
      (4)客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。

    3.使用场景:

      (1)有多个对象可以处理同一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的。这种情况可以使用责任链模式,把处理请求的对象实现成为责任对象,然后把它们构成一个责任链,当请求在这个链中传递的时候,具体由哪个责任对象来处理,会在运行时动态判断。
      (2)你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求的话,可以使用责任链模式,责任链模式实现了请求者和接收者之间的解耦,请求者不需要知道究竟是哪一个接收者对象来处理了请求。
      (3)想要动态指定处理一个请求的对象集合,可以使用责任链模式,责任链模式能动态的构建责任链,也就是动态的来决定到底哪些责任对象来参与到处理请求中来,相当于是动态指定了处理一个请求的责任对象集合。
      (4)使用if…else…语句,代码看上去很糟糕时,就可以使用责任链模式实现。

    4.模式实现:

       下面使用了一个责任链模式的最简单的实现。
    责任链模式涉及到的角色如下所示:
     

     (1)抽象处理者(Handler)角色:

      定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
    public abstract class Handler {
        /**
         * 持有后继的责任对象
         */
        protected Handler successor;
        /**
         * 示意处理请求的方法,虽然这个示意方法是没有传入参数的
         * 但实际是可以传入参数的,根据具体需要来选择是否传递参数
         */
        public abstract void handleRequest();
        /**
         * 取值方法
         */
        public Handler getSuccessor() {
            return successor;
        }
        /**
         * 赋值方法,设置后继的责任对象
         */
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
    }

     (2)具体处理者(ConcreteHandler)角色:

      具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
    public class ConcreteHandler extends Handler {
        /**
         * 处理方法,调用此方法处理请求
         */
        @Override
        public void handleRequest() {
            /**
             * 判断是否有后继的责任对象
             * 如果有,就转发请求给后继的责任对象
             * 如果没有,则处理请求
             */
            if(getSuccessor() != null)
            {            
                System.out.println("放过请求");
                getSuccessor().handleRequest();            
            }else
            {            
                System.out.println("处理请求");
            }
        }
    }

      (3)客户端类

    public class Client {
        public static void main(String[] args) {
            //组装责任链
            Handler handler1 = new ConcreteHandler();
            Handler handler2 = new ConcreteHandler();
            handler1.setSuccessor(handler2);
            //提交请求
            handler1.handleRequest();
        }
    }
      可以看出,客户端创建了两个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,而第二个处理者对象没有下家。然后客户端将请求传递给第一个处理者对象。
      由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理。因此,第一个处理者对象接到请求后,会将请求传递给第二个处理者对象。由于第二个处理者对象没有下家,于是自行处理请求。活动时序图如下所示。

    5.优缺点:

           (1)责任链模式的优点:

         [1]请求者和接收者松散耦合:在责任链模式里面,请求者并不知道接收者是谁,也不知道具体如何处理,请求者只是负责向责任链发出请求就可以了。而每个责任对象也不用管请求者或者是其它的责任对象,只负责处理自己的部分,其它的就交由其它的责任对象去处理。也就是说,请求者和接收者是完全解耦的。
        [2]动态组合责任:责任链模式会把功能处理分散到单独的责任对象里面,然后在使用的时候,可以动态组合责任形成责任链,从而可以灵活的给对象分配责任,也可以灵活的实现和改变对象的责任。
        [3]简化了对象:请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。
        [4]增加新的请求处理类很方便:在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。
     

      (2)责任链模式的缺点:

        [1]产生很多细粒度对象:责任链模式会把功能处理分散到单独的责任对象里面,也就是每个责任对象只是处理一个方面的功能,要把整个业务处理完,需要大量的责任对象的组合,这会产生大量的细粒度责任对象。
        [2]不一定能被处理:责任链模式的每个责任对象只负责自己处理的那一部分,因此可能会出现某个请求,把整个链传递完了,都没有责任对象处理它。这就需要在使用责任链模式的时候注意,需要提供默认的处理,并且注意构建的链的有效性。
        [3]可能造成循环调用:如果建链不当,可能会造成循环调用,将导致系统陷入死循环。
        [4]调试不方便:对于比较长的责任链,请求的处理可能涉及到多个处理对象,在进行代码调试时不太方便,有碍于除错。
        [5]不能保证请求一定被接收:既然一个请求没有明确的接收者,那么就不能保证它一定会被处理 —该请求可能一直到链的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到处理。
        [6] 系统性能将受到一定影响:而且在进行代码调试时不太方便;可能会造成循环调用。

    6. 纯与不纯的责任链模式

      (1)纯的责任链模式:

      一个具体处理者角色处理只能对请求作出两种行为中的一个:一个是自己处理(承担责任),另一个是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又将责任向下传的情况。请求在责任链中必须被处理,不能出现无果而终的结局。

      (2)反之就是不纯的责任链模式。  

      在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。

    7.责任链模式与状态模式对比

      状态模式也好,责任链模式也罢,都能解耦和优化大量的逻辑判断,如:if…else…语句。
      责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象练成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。各个责任类不知道也没必要知道下一个责任对象是谁!由客户端统一设置顺序和谁连接到链条,谁不连接到链条…… 可以看出,责任链在client(客户端)连接,责任链模式要比状态模式灵活很多。
      但是,这时候有人要问,既然他们都可以解决逻辑判断的分支过多的问题,那么,是不是责任链模式比状态模式好呢?
      责任链模式过于灵活,在客户端使用时,需要客户端去确定下一个对象是谁,一些列的set操作…… 在多次设置的时候很容易出问题。
      另外状态模式是一个对象的内在状态发生改变,而责任链模式是多个对象之间的改变,这也说明他们两个模式处理的情况不同。
    小结:
      个人认为,责任链模式与状态模式的最大的不同是设置自己的下一级的问题上,状态模式是在类的设计阶段就定好的,不能在客户端改变,而责任链的下一级是在客户端自己来确定的。
      状态模式:是让各个状态对象自己知道其下一个处理的对象是谁,即在编译时便设定。相当于If ,else-if,else-if……, 设计思路是把逻辑判断转移到各个State类的内部实现(相当于If,else If),执行时客户端通过调用环境—Context类的方法来间接执行状态类的行为,客户端不直接和状态交互。
      责任链模式:各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定某个类型的链条,请求发出后穿越链条,直到被某个责任类处理或者链条结束。设计思路是把各个业务逻辑判断封装到不同责任类,且携带下一个责任的对应引用,但不像状态模式那样需要明确知道这个引用指向谁,而是在客户端设置链接方式或者过程。使用时,向链的第一个子类的执行方法传递参数就可以。客户端去通过环境类调用责任链,全自动运转起来。

    8.注意事项

      在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
       责任链模式的主要优点在于可以降低系统的耦合度,简化对象的相互连接,同时增强给对象指派责任的灵活性,增加新的请求处理类也很方便;其主要缺点在于不能保证请求一定被接收,且对于比较长的责任链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

    9.应用实例

    (1)JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,
    (2)Java中的异常机制就是一种责任链模式,catch链就是一种责任链。
    (3)JavaScript中的时间冒泡和捕获机制。
    (4)Servlet中的过滤器的链式处理。
    (5)Structs2中拦截器的调用。
    (6)Tomcat中的Filter就是使用了责任链模式,还有Tomcat中Valve链...

    10.代码实例:

      来考虑这样一个功能:申请聚餐费用的管理。
        很多公司都是这样的福利,就是项目组或者是部门可以向公司申请一些聚餐费用,用于组织项目组成员或者是部门成员进行聚餐活动。
        申请聚餐费用的大致流程一般是:由申请人先填写申请单,然后交给领导审批,如果申请批准下来,领导会通知申请人审批通过,然后申请人去财务领取费用,如果没有批准下来,领导会通知申请人审批未通过,此事也就此作罢。
        不同级别的领导,对于审批的额度是不一样的,比如,项目经理只能审批500元以内的申请;部门经理能审批1000元以内的申请;而总经理可以审核任意额度的申请。
        也就是说,当某人提出聚餐费用申请的请求后,该请求会经由项目经理、部门经理、总经理之中的某一位领导来进行相应的处理,但是提出申请的人并不知道最终会由谁来处理他的请求,一般申请人是把自己的申请提交给项目经理,或许最后是由总经理来处理他的请求。  
        可以使用责任链模式来实现上述功能:当某人提出聚餐费用申请的请求后,该请求会在 项目经理-->部门经理-->总经理 这样一条领导处理链上进行传递,发出请求的人并不知道谁会来处理他的请求,每个领导会根据自己的责任范围,来判断是处理请求还是把请求交给更高级别的领导,只要有领导处理了,传递就结束了。
        需要把每位领导的处理独立出来,实现成单独的责任处理对象,然后为它们提供一个公共的、抽象的父责任对象,这样就可以在客户端来动态地组合责任链,实现不同的功能要求了。

      (1)抽象处理者角色类

    public abstract class Handler {
        /**
         * 持有下一个处理请求的对象
         */
        protected Handler successor = null;
        /**
         * 取值方法
         */
        public Handler getSuccessor() {
            return successor;
        }
        /**
         * 设置下一个处理请求的对象
         */
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
        /**
         * 处理聚餐费用的申请
         * @param user    申请人
         * @param fee    申请的钱数
         * @return        成功或失败的具体通知
         */
        public abstract String handleFeeRequest(String user , double fee);
    }

         (2)具体处理者角色

    public class ProjectManager extends Handler {
        @Override
        public String handleFeeRequest(String user, double fee) {        
            String str = "";
            //项目经理权限比较小,只能在500以内
            if(fee < 500)
            {
                //为了测试,简单点,只同意张三的请求
                if("张三".equals(user))
                {
                    str = "成功:项目经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";    
                }else
                {
                    //其他人一律不同意
                    str = "失败:项目经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
                }
            }else
            {
                //超过500,继续传递给级别更高的人处理
                if(getSuccessor() != null)
                {
                    return getSuccessor().handleFeeRequest(user, fee);
                }
            }
            return str;
        }
    
    }
    public class DeptManager extends Handler {
        @Override
        public String handleFeeRequest(String user, double fee) {
            
            String str = "";
            //部门经理的权限只能在1000以内
            if(fee < 1000)
            {
                //为了测试,简单点,只同意张三的请求
                if("张三".equals(user))
                {
                    str = "成功:部门经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";    
                }else
                {
                    //其他人一律不同意
                    str = "失败:部门经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
                }
            }else
            {
                //超过1000,继续传递给级别更高的人处理
                if(getSuccessor() != null)
                {
                    return getSuccessor().handleFeeRequest(user, fee);
                }
            }
            return str;
        }
    
    }
    public class GeneralManager extends Handler {
        @Override
        public String handleFeeRequest(String user, double fee) {
            
            String str = "";
            //总经理的权限很大,只要请求到了这里,他都可以处理
            if(fee >= 1000)
            {
                //为了测试,简单点,只同意张三的请求
                if("张三".equals(user))
                {
                    str = "成功:总经理同意【" + user + "】的聚餐费用,金额为" + fee + "元";    
                }else
                {
                    //其他人一律不同意
                    str = "失败:总经理不同意【" + user + "】的聚餐费用,金额为" + fee + "元";
                }
            }else
            {
                //如果还有后继的处理对象,继续传递
                if(getSuccessor() != null)
                {
                    return getSuccessor().handleFeeRequest(user, fee);
                }
            }
            return str;
        }
    
    }

      (3)客户端类

    public class Client {
        public static void main(String[] args) {
            //先要组装责任链
            Handler h1 = new GeneralManager();
            Handler h2 = new DeptManager();
            Handler h3 = new ProjectManager();
            h3.setSuccessor(h2);
            h2.setSuccessor(h1);
            
            //开始测试
            String test1 = h3.handleFeeRequest("张三", 300);
            System.out.println("test1 = " + test1);
            String test2 = h3.handleFeeRequest("李四", 300);
            System.out.println("test2 = " + test2);
            System.out.println("---------------------------------------");
            
            String test3 = h3.handleFeeRequest("张三", 700);
            System.out.println("test3 = " + test3);
            String test4 = h3.handleFeeRequest("李四", 700);
            System.out.println("test4 = " + test4);
            System.out.println("---------------------------------------");
            
            String test5 = h3.handleFeeRequest("张三", 1500);
            System.out.println("test5 = " + test5);
            String test6 = h3.handleFeeRequest("李四", 1500);
            System.out.println("test6 = " + test6);
        }
    
    }
     
    运行结果如下所示:
     

    11.责任链模式在Tomcat中的应用

     
      众所周知Tomcat中的Filter就是使用了责任链模式,创建一个Filter除了要在web.xml文件中做相应配置外,还需要实现javax.servlet.Filter接口。
    public class TestFilter implements Filter{
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            chain.doFilter(request, response);
        }
        public void destroy() {
        }
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    }
    使用DEBUG模式所看到的结果如下:
     其实在真正执行到TestFilter类之前,会经过很多Tomcat内部的类。顺带提一下其实Tomcat的容器设置也是责任链模式,注意被红色方框所圈中的类,从Engine到Host再到Context一直到Wrapper都是通过一个链传递请求。被绿色方框所圈中的地方有一个名为ApplicationFilterChain的类,ApplicationFilterChain类所扮演的就是抽象处理者角色,而具体处理者角色由各个Filter扮演。

      (1)第一个疑问是ApplicationFilterChain将所有的Filter存放在哪里?

    答案是保存在ApplicationFilterChain类中的一个ApplicationFilterConfig对象的数组中。
       /**
         * Filters.
         */
        private ApplicationFilterConfig[] filters =  new ApplicationFilterConfig[0];

      (2)那ApplicationFilterConfig对象又是什么呢?

    ApplicationFilterConfig是一个Filter容器。以下是ApplicationFilterConfig类的声明:
    /**
     * Implementation of a <code>javax.servlet.FilterConfig</code> useful in
     * managing the filter instances instantiated when a web application
     * is first started.
     *
     * @author Craig R. McClanahan
     * @version $Id: ApplicationFilterConfig.java 1201569 2011-11-14 01:36:07Z kkolinko $
     */
      当一个web应用首次启动时ApplicationFilterConfig会自动实例化,它会从该web应用的web.xml文件中读取配置的Filter的信息,然后装进该容器。

      (3)刚刚看到在ApplicationFilterChain类中所创建的ApplicationFilterConfig数组长度为零,那它是在什么时候被重新赋值的呢?

     private ApplicationFilterConfig[] filters =  new ApplicationFilterConfig[0];
      是在调用ApplicationFilterChain类的addFilter()方法时。
        /**
         * The int which gives the current number of filters in the chain.
         */
        private int n = 0;
        public static final int INCREMENT = 10;
    调用:
        void addFilter(ApplicationFilterConfig filterConfig) {
    
            // Prevent the same filter being added multiple times
            for(ApplicationFilterConfig filter:filters)
                if(filter==filterConfig)
                    return;
    
            if (n == filters.length) {
                ApplicationFilterConfig[] newFilters =
                    new ApplicationFilterConfig[n + INCREMENT];
                System.arraycopy(filters, 0, newFilters, 0, n);
                filters = newFilters;
            }
            filters[n++] = filterConfig;
    
        }
      变量n用来记录当前过滤器链里面拥有的过滤器数目,默认情况下n等于0,ApplicationFilterConfig对象数组的长度也等于0,所以当第一次调用addFilter()方法时,if (n ==filters.length)的条件成立,ApplicationFilterConfig数组长度被改变。之后filters[n++] = filterConfig;将变量filterConfig放入ApplicationFilterConfig数组中并将当前过滤器链里面拥有的过滤器数目+1。

      (4)那ApplicationFilterChain的addFilter()方法又是在什么地方被调用的呢?

      是在ApplicationFilterFactory类的createFilterChain()方法中。
      1 public ApplicationFilterChain createFilterChain
      2         (ServletRequest request, Wrapper wrapper, Servlet servlet) {
      3 
      4         // get the dispatcher type
      5         DispatcherType dispatcher = null; 
      6         if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
      7             dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR);
      8         }
      9         String requestPath = null;
     10         Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
     11         
     12         if (attribute != null){
     13             requestPath = attribute.toString();
     14         }
     15         
     16         // If there is no servlet to execute, return null
     17         if (servlet == null)
     18             return (null);
     19 
     20         boolean comet = false;
     21         
     22         // Create and initialize a filter chain object
     23         ApplicationFilterChain filterChain = null;
     24         if (request instanceof Request) {
     25             Request req = (Request) request;
     26             comet = req.isComet();
     27             if (Globals.IS_SECURITY_ENABLED) {
     28                 // Security: Do not recycle
     29                 filterChain = new ApplicationFilterChain();
     30                 if (comet) {
     31                     req.setFilterChain(filterChain);
     32                 }
     33             } else {
     34                 filterChain = (ApplicationFilterChain) req.getFilterChain();
     35                 if (filterChain == null) {
     36                     filterChain = new ApplicationFilterChain();
     37                     req.setFilterChain(filterChain);
     38                 }
     39             }
     40         } else {
     41             // Request dispatcher in use
     42             filterChain = new ApplicationFilterChain();
     43         }
     44 
     45         filterChain.setServlet(servlet);
     46 
     47         filterChain.setSupport
     48             (((StandardWrapper)wrapper).getInstanceSupport());
     49 
     50         // Acquire the filter mappings for this Context
     51         StandardContext context = (StandardContext) wrapper.getParent();
     52         FilterMap filterMaps[] = context.findFilterMaps();
     53 
     54         // If there are no filter mappings, we are done
     55         if ((filterMaps == null) || (filterMaps.length == 0))
     56             return (filterChain);
     57 
     58         // Acquire the information we will need to match filter mappings
     59         String servletName = wrapper.getName();
     60 
     61         // Add the relevant path-mapped filters to this filter chain
     62         for (int i = 0; i < filterMaps.length; i++) {
     63             if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
     64                 continue;
     65             }
     66             if (!matchFiltersURL(filterMaps[i], requestPath))
     67                 continue;
     68             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
     69                 context.findFilterConfig(filterMaps[i].getFilterName());
     70             if (filterConfig == null) {
     71                 // FIXME - log configuration problem
     72                 continue;
     73             }
     74             boolean isCometFilter = false;
     75             if (comet) {
     76                 try {
     77                     isCometFilter = filterConfig.getFilter() instanceof CometFilter;
     78                 } catch (Exception e) {
     79                     // Note: The try catch is there because getFilter has a lot of 
     80                     // declared exceptions. However, the filter is allocated much
     81                     // earlier
     82                     Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
     83                     ExceptionUtils.handleThrowable(t);
     84                 }
     85                 if (isCometFilter) {
     86                     filterChain.addFilter(filterConfig);
     87                 }
     88             } else {
     89                 filterChain.addFilter(filterConfig);
     90             }
     91         }
     92 
     93         // Add filters that match on servlet name second
     94         for (int i = 0; i < filterMaps.length; i++) {
     95             if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
     96                 continue;
     97             }
     98             if (!matchFiltersServlet(filterMaps[i], servletName))
     99                 continue;
    100             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
    101                 context.findFilterConfig(filterMaps[i].getFilterName());
    102             if (filterConfig == null) {
    103                 // FIXME - log configuration problem
    104                 continue;
    105             }
    106             boolean isCometFilter = false;
    107             if (comet) {
    108                 try {
    109                     isCometFilter = filterConfig.getFilter() instanceof CometFilter;
    110                 } catch (Exception e) {
    111                     // Note: The try catch is there because getFilter has a lot of 
    112                     // declared exceptions. However, the filter is allocated much
    113                     // earlier
    114                 }
    115                 if (isCometFilter) {
    116                     filterChain.addFilter(filterConfig);
    117                 }
    118             } else {
    119                 filterChain.addFilter(filterConfig);
    120             }
    121         }
    122 
    123         // Return the completed filter chain
    124         return (filterChain);
    125 
    126     }
      可以将如上代码分为两段,51行之前为第一段,51行之后为第二段。
      第一段的主要目的是创建ApplicationFilterChain对象以及一些参数设置。
      第二段的主要目的是从上下文中获取所有Filter信息,之后使用for循环遍历并调用filterChain.addFilter(filterConfig);将filterConfig放入ApplicationFilterChain对象的ApplicationFilterConfig数组中。

      (5)那ApplicationFilterFactory类的createFilterChain()方法又是在什么地方被调用的呢?

     是在StandardWrapperValue类的invoke()方法中被调用的。
      
     
    由于invoke()方法较长,所以将很多地方省略。
     public final void invoke(Request request, Response response)
            throws IOException, ServletException {
       ...省略中间代码
         // Create the filter chain for this request
            ApplicationFilterFactory factory =
                ApplicationFilterFactory.getInstance();
            ApplicationFilterChain filterChain =
                factory.createFilterChain(request, wrapper, servlet);
      ...省略中间代码
             filterChain.doFilter(request.getRequest(), response.getResponse());
      ...省略中间代码
        }
      那正常的流程应该是这样的:
      在StandardWrapperValue类的invoke()方法中调用ApplicationFilterChai类的createFilterChain()方法 --> 在ApplicationFilterChai类的createFilterChain()方法中调用ApplicationFilterChain类的addFilter()方法 --> 在ApplicationFilterChain类的addFilter()方法中给ApplicationFilterConfig数组赋值。
    根据上面的代码可以看出StandardWrapperValue类的invoke()方法在执行完createFilterChain()方法后,会继续执行ApplicationFilterChain类的doFilter()方法,然后在doFilter()方法中会调用internalDoFilter()方法。
      以下是internalDoFilter()方法的部分代码
            // Call the next filter if there is one
            if (pos < n) {
           //拿到下一个Filter,将指针向下移动一位
                //pos它来标识当前ApplicationFilterChain(当前过滤器链)执行到哪个过滤器
                ApplicationFilterConfig filterConfig = filters[pos++];
                Filter filter = null;
                try {
              //获取当前指向的Filter的实例
                    filter = filterConfig.getFilter();
                    support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                              filter, request, response);
                    
                    if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                            filterConfig.getFilterDef().getAsyncSupported())) {
                        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                                Boolean.FALSE);
                    }
                    if( Globals.IS_SECURITY_ENABLED ) {
                        final ServletRequest req = request;
                        final ServletResponse res = response;
                        Principal principal = 
                            ((HttpServletRequest) req).getUserPrincipal();
    
                        Object[] args = new Object[]{req, res, this};
                        SecurityUtil.doAsPrivilege
                            ("doFilter", filter, classType, args, principal);
                        
                    } else {
                //调用Filter的doFilter()方法  
                        filter.doFilter(request, response, this);
                    }
      这里的filter.doFilter(request, response, this);就是调用我们前面创建的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法会继续调用chain.doFilter(request, response);方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,这样执行直到里面的过滤器全部执行。
      如果定义两个过滤器,则Debug结果如下:
     
     
     
     
  • 相关阅读:
    java-connect-mysql
    搜索框提示列表问题
    方法中的函数会掩盖this,解决办法!
    关于W3C盒子布局
    将类数组转化成数组
    js获取元素宽高
    使用gulp添加版本号
    flex布局
    排序-冒泡排序
    js事件、自定义dom事件、自定义事件
  • 原文地址:https://www.cnblogs.com/yysbolg/p/7461440.html
Copyright © 2011-2022 走看看