java-设计模式-责任链

纯的与不纯的责任链模式

  一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又 把责任向下传的情况。

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

  纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的。但是在实际的系统里,纯的责任链很难找到。如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。

责任链模式在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扮演。

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

  答案是保存在ApplicationFilterChain类中的一个ApplicationFilterConfig对象的数组中。

    /**
     * Filters.
     */
    private ApplicationFilterConfig[] filters =
        new ApplicationFilterConfig[0];

  那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的信息,然后装进该容器。

  刚刚看到在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。

  那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数组中。

  那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结果如下:

  

 

 

 

时间: 2024-08-31 22:21:00

java-设计模式-责任链的相关文章

Java设计模式--责任链模式

责任链模式 使多个对象都有机会都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. Chain of Responsibility Pattern A void coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiv

iOS设计模式 - 责任链

iOS设计模式 - 责任链   原理图   说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任.   源码 https://github.com/YouXianMing/iOS-Design-Patterns // // ChainOfResponsibilityProtocol.h // C

深入浅出基于Java的责任链模式

一.引言 初看责任链模式,心里不禁想起了一个以前听过的相声:看牙.说的是一个病人看牙的时候,医生不小心把拔下的一个牙掉进了病人嗓子里.病人因此楼上楼下的跑了好多科室,最后无果而终. 责任链模式就是这种"推卸"责任的模式,你的问题在我这里能解决我就解决,不行就把你推给另一个对象.至于到底谁解决了这个问题了呢?我管呢! 二.定义与结构 从名字上大概也能猜出这个模式的大概模样--系统中将会存在多个有类似处理能力的对象.当一个请求触发后,请求将在这些对象组成的链条中传递,直到找到最合适的&qu

13、Python与设计模式--责任链模式

一.请假系统 假设有这么一个请假系统:员工若想要请3天以内(包括3天的假),只需要直属经理批准就可以了:如果想请3-7天,不仅需要直属经理批准,部门经理需要最终批准:如果请假大于7天,不光要前两个经理批准,也需要总经理最终批准.类似的系统相信大家都遇到过,那么该如何实现呢?首先想到的当然是if-else-,但一旦遇到需求变动,其臃肿的代码和复杂的耦合缺点都显现出来.简单分析下需求,"假条"在三个经理间是单向传递关系,像一条链条一样,因而,我们可以用一条"链"把他们进

设计模式之禅之设计模式-责任链模式

一:责任链模式的定义        --->使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止.        --->责任链模式的重点是在"链"上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果        --->一般会有一个封装类对责任模式进行封装,也就是替代Client类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了

实例讲解Java的设计模式编程中责任链模式的运用_java

定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止.类型:行为类模式类图: 首先来看一段代码: public void test(int i, Request request){ if(i==1){ Handler1.response(request); }else if(i == 2){ Handler2.response(request); }else if(i == 3){ Handler3.r

Java设计模式编程中的责任链模式使用示例_java

责任链模式:多个对象由其对象对应下家的引用连成一条链,请求在这个链上传递,直到 链上的某一个接收对象处理此请求.因为请求的客户端并不知道链上最终是谁来处理这个请求,使得系统可以在不影响客户端的情况下动态地重新组织和分配责任, 从而避免了请求发送者与请求处理者之间的耦合. 责任链械中涉及到三种角色: 1,抽象处理者角色 2,具体处理者角色 3,请求发送者 小例子:假设去买房子,买房子就需要砍价, 卖房的人职位不同,可以优惠的价格也不同,不同职位就可以形成一个处理请求的链.我们暂定: * 基层销售员

Java设计模式之责任链模式简介_java

对于使用过宏的朋友应该知道,利用宏可以实现一个键绑定多个技能.例如如果排在前面的技能有CD,则跳过此技能,执行之后的技能.记得曾经玩DK,打怪的时候,就是用一个键,一直按就行了.在servlet里的doGet和doPost方法,我们一般都把doGet请求发动到doPost里来处理,这也是一种责任链的模式. 这里,有个宏,绑定了"冰血冷脉"和"寒冰箭"两个技能,程序实例如下所示: package responsibility; /** * DOC 技能接口,要绑定的技

Java责任链设计模式_java

责任链(Chain of Responsibility)模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上 传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态 地重新组织和分配责任. 责任链模式属于行为型设计模式之一,怎么理解责任链?责任链是可以理解成数个对象首尾连接而成,每一个节点就是一个对象,每个对象对应不同的处理逻辑,直至有一个对象响应

轻松掌握java责任链模式_java

定义:责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的设计模式属于行为型模式.在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推. 特点:1.降低耦合度.它将请求的发送者和接收者解耦.              2.简化了对象.使得对象不需要知道链的结构.              3.增强给