设计模式-责任链模式

1.定义

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止 

2. 使用场景 

一个请求需要被多个对象中的某一个处理,但是到底是哪个对象必须在运行时根据条件决定。 

在这里插入图片描述

可以看到责任链模式只有两个角色:

1. Handler:处理器类的接口

2. ConcreteHandler x:处理器实现类,里面定义了下一个处理器以及处理方法

3. 举例分析 

 层层上报,最终会由校长进行审批

正常代码书写

// 辅导员审批
if (isTrue) {
    instructor.approve();
    // 系主任审批
    if (isOk) {
        departmentHead.approve();
        // 院长审批
        if (isGoodEnough) {
            dean.approve();
            // 校长审批
            if (isGood) {
                headmaster.approve();
            } else {
                System.out.println("很遗憾,未通过全校公示");
            }
        } else {
            System.out.println("很遗憾,未在学院评选中脱颖而出");
        }
    } else {
        System.out.println("该生表现欠佳,不予通过");
    }
} else {
    System.out.println("信息填写有误,请重新认真填写");
}

可以通过责任链模式进行处理,如果不满足要求直接停止处理,如果满足则向下传递处理直至到达链末尾

4. 最终实现

在自己权限下自己处理,超过权限范围上交处理

第一步:创建抽象处理者

@Data
public abstract class Handler {
    private Handler next;
    public abstract void handleRequest(String name, int days);
}

 第二步:创建组长、总监、部长三个具体处理者,实现具体的处理逻辑

public class PMHandler extends Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 3) {
            System.out.println(name + ",组长已经同意您的请假审批!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(name, days);
            } else {
                System.out.println("请假天数太多,申请被驳回!");
            }
        }
    }
}

public class DirectorHandler extends Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 7) {
            System.out.println(name + ",中心总监已经同意您的请假审批");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(name, days);
            } else {
                System.out.println("请假天数太多,申请被驳回!");
            }
        }
    }
}

public class MinisterHandler extends Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 15) {
            System.out.println(name + ",部长已经同意您的请假审批");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(name, days);
            } else {
                System.out.println("请假天数太多,申请被驳回!");
            }
        }
    }
}

 第三步:创建测试类,在类中创建并使用责任链(向责任链传递请求)

public class OASystem {
    public static void main(String[] args) {
        // 创建具体处理者
        Handler pm = new PMHandler();
        Handler director = new DirectorHandler();
        Handler minister = new MinisterHandler();
        // 构建责任链
        pm.setNext(director);
        director.setNext(minister);

        // 使用责任链
        pm.handleRequest("张三", 5);
    }
}

 5. 代码改进

上面代码如果增加、删除或者调整顺序很不方便,还需要维护处理器和处理器的前后顺序关系 

基于List构建隐形的责任链 

// 抽象处理者
public interface Handler {
    public abstract void handleRequest(String name, int days);
}

// 具体处理者
public class PMHandler implements Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 3) {
            System.out.println(name +",组长已经同意您的请假审批!");
        }
    }
}

public class DirectorHandler implements Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 7) {
            System.out.println(name + ",中心总监已经同意您的请假审批");
        }
    }
}

public class MinisterHandler implements Handler {
    @Override
    public void handleRequest(String name, int days) {
        if (days <= 15) {
            System.out.println(name + ",部长已经同意您的请假审批");
        }
    }
}

// 责任链类
public class HandlerChain implements Handler {
    private List<Handler> handlerList;

    public HandlerChain() {
        this.handlerList = new ArrayList<>();
    }

    public HandlerChain addHandler(Handler handler) {
        handlerList.add(handler);
        return this;
    }


    @Override
    public void handleRequest(String name, int days) {
        for (Handler handler : handlerList) {
            handler.handleRequest(name, days);
        }
    }
}

// 客户类
public class OASystem {
    public static void main(String[] args) {
        // 创建具体处理者
        Handler pm = new PMHandler();
        Handler director = new DirectorHandler();
        Handler minister = new MinisterHandler();
        // 构建责任链
        HandlerChain chain = new HandlerChain()
                .addHandler(pm)
                .addHandler(director)
                .addHandler(minister);

        // 使用责任链
        chain.handleRequest("王二", 10);
    }
}

 这块原来那个作者对于增加或者删除以及改变顺序的做法是这样的,使用list记录责任链

  • 可以通过set操作,将组长换成方向负责人
  • 可以通过swap操作,调整部长和总监的审批顺序(不太恰当的例子😂)
  • 可以通过add(index, element)操作,在指定位置增加handler
  • 可以通过remove操作删除handler

6. 最终版的责任链 

当时在公司里看这段代码的时候,感觉并不是责任链,直到了解了上面责任链的演化过程,才知道这段代码也是,这里面通过spring将所有的处理器放到List集合中,然后对集合进行遍历处理,通过@order注解来决定执行顺序,处理器里面对方法进行重写,对具体业务逻辑功能书写

处理器抽象类 

public abstract class AbstractTestHandler {
    // 测试处理器
    public abstract void handleTestInfo(HandlerObject handlerObject);
}

 具体对哪个对象进行处理

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class HandlerObject {
    private Handler01 handler01;

    private Handler02 handler02;
}

两个随意定义的类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Handler01 {
    private Integer age;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Handler02 {
    private String name;
}

实现类,对某个对象进行不同的业务处理

@Component
@Order(1)
public class Handler01Handler extends AbstractTestHandler {
    @Override
    public void handleTestInfo(HandlerObject handlerObject) {
        handlerObject.getHandler01().setAge(12);
    }
}
@Component
@Order(2)
public class Handler02Handler extends AbstractTestHandler {
    @Override
    public void handleTestInfo(HandlerObject handlerObject) {
        handlerObject.getHandler02().setName("test");
    }
}

操作类,对处理器进行遍历实现功能的

@Component
public class TestOperator {
    @Autowired
    List<AbstractTestHandler> testHandlers;

    public void execute(HandlerObject handlerObject) {
        testHandlers.forEach(resourceHandler -> resourceHandler.handleTestInfo(handlerObject));
    }
}

测试类

public void test01(){
        HandlerObject handlerObject = new HandlerObject();
        handlerObject.setHandler02(Handler02.builder().build());
        handlerObject.setHandler01(Handler01.builder().build());
        testOperator.execute(handlerObject);
        System.out.println(handlerObject);
    }

后面我又试了一下,不用抽象类,用接口定义也是可以的 

7. 优缺点

优点:

降低了对象之间的耦合度:具体业务功能指派给指定处理器进行处理,降低代码冗余

增强了系统的可扩展性:更改代码的话直接在特定处理器内进行更改就可以 

缺点:

按之前那种next调用的话,配错有可能出现循环调用

8. 源码学习责任链 

基于tomcat源码进行学习

<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>8.0.41</version>
    </dependency>
    <!-- Thanks for using https://jar-download.com -->
</dependencies>

过滤器的作用:

  • 通过Filter可以对请求进行预处理,比如ip地址白名单,决定客户端的请求是否被处理,RemoteHostFilter
  • 通过Filter还能对响应结果进行处理,比如在响应头中添加信息,HttpHeaderSecurityFilter

过滤器主要使用途径

Filter在请求进入容器后、未进入servlet之前,对请求进行预处理;同时,Filter可以在servlet返回响应后、未返回给客户端之前,对响应进行处理 

tomcat的源码中,定义了Filter接口 

public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;
    public void destroy();
}
  • init()方法:servlet容器在实例化Filter对象后、调用Filter对象的doFilter方法前,调用init()方法(仅一次)以表明该filter可以提供服务

  • doFilter()方法:

    请求/响应对(pair)在FilterChain上传递,调用链上filter的doFilter()方法可以处理请求/响应对
    方法入参中的FilterChain 是构成责任链的关键,通过调用chain的doFilter()方法,可以实现请求/响应对的传递
    FilterChain的doFilter()方法中,会通过例如数组下标pos的形式调用下一个filter
    因此,形成了一个调用链(责任链):chain.doFilter()调用filter1 --> filter1.doFilter()执行处理逻辑、调用chain.doFilter() --> chain.doFilter()调用filter2 --> filter2.doFilter()执行处理逻辑、调用chain.doFilter() --> …

  • destroy()方法:

    • 容器调用filter的destroy()方法,以表明该filter不再提供服务
    • 仅当filter的doFilter()方法中,所有线程都退出或超时,容器才会调用destroy()

FilterChain 

FilterChain接口是filter责任链接口,接口中doFilter()方法的实现逻辑可以保证请求/响应对在链上的传递与处理 

public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;
}
  • 如果存在下一个filter,则调用链上的下一个filter处理请求/响应对
  • 如果不存在下一个filter,则调用链末尾的资源,如servlet的service()方法

ApplicationFilterChain

ApplicationFilterChain有四个重要的成员变量:

  • filters:ApplicationFilterConfig类型的数组,记录FilterChain上的filter;初始化长度为0,使用时按照INCREMENT = 10的增量进行扩容
  • pos:保存FilterChain当前位置的int变量,实际就是filters数组的索引,可以指向某个filter
  • n:FilterChain的实际大小,也就是filters数组中存储的filter的数量;filters数组每次按照10进行扩容,这是filters数组的容量,并非filter的数量
  • servlet:FilterChain上的filter访问到链尾时,将执行servlet实例的service()方法;通过servlet的service()方法,实现对用户请求的处理 
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private int pos = 0;
private int n = 0;
private Servlet servlet = null;

ApplicationFilterChain相关方法的简单介绍:

ApplicationFilterFactory的createFilterChain()是一个工厂方法,负责完成ApplicationFilterChain的创建,包括设置servlet、向链中添加filter等

public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
	// 为FilterChain添加filter的关键代码
	ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName());
	// ...,其他代码省略
	filterChain.addFilter(filterConfig);
}

ApplicationFilterChain的addFilter()方法,被createFilterChain()方法调用,涉及filter是否重复添加的校验、filters数组的扩容、filter的添加

void addFilter(ApplicationFilterConfig filterConfig) {}

 doFilter()方法(重头戏)

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    if( Globals.IS_SECURITY_ENABLED ) {
        final ServletRequest req = request;
        final ServletResponse res = response;
        try {
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedExceptionAction<Void>() {
                    @Override
                    public Void run()
                        throws ServletException, IOException {
                        internalDoFilter(req,res);
                        return null;
                    }
                }
            );
        } catch( PrivilegedActionException pe) {
            // 省略异常的处理
        }
    } else {
        internalDoFilter(request,response);
    }
}

internalDoFilter()方法:

1. 利用pos变量,从头开始逐个访问FilterChain上的filter,调用filter的doFilter()方法处理请求/响应对

2. 如果FilterChain上的所有所有filter都已经执行完毕,则执行servlet的service()方法处理请求

private void internalDoFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {
    // Call the next filter if there is one
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = null;
        try {
            filter = filterConfig.getFilter();
            // ... 省略代码
            if( Globals.IS_SECURITY_ENABLED ) {
                // ... 省略代码
                SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
            } else {
                filter.doFilter(request, response, this);
            }

            support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                      filter, request, response);
        } 
        // ... 省略catch
        return; // 巧妙的return处理,后续会提到原因
    }

    try {
        // ... 省略代码
        if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
            if( Globals.IS_SECURITY_ENABLED ) {
                // ... 省略代码
                Object[] args = new Object[]{req, res};
                SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
            } else {
                servlet.service(request, response);
            }
        } else {
            servlet.service(request, response);
        }
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response);
    } 
    // ... 省略catch和finally
}
  • 笔者在看到这里时,整个人是懵逼的:pos的更新确实能沿着FilterChain调用其上的filter,问题是开头是if (pos < n)并非while(pos < n),这咋能实现循环呢?别骗我读书少哦 😠
  • 耐下性子,点开filter的doFilter()方法,发现奥妙就在这里:filter的doFilter()方法,都会在合适的时机调用传入的chain的doFilter()方法
  • 例如,FailedRequestFilter的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (!isGoodRequest(request)) {
        FailReason reason = (FailReason) request.getAttribute(Globals.PARAMETER_PARSE_FAILED_REASON_ATTR);

        int status;

        switch (reason) {
            // 为各种错误类型设置status
        }
        // 执行失败,直接返回携带错误信息的响应
        ((HttpServletResponse) response).sendError(status);
        return;
    }
    // 执行成功,继续访问FilterChain上的下一个filter
    chain.doFilter(request, response);
}

RemoteHostFilter的doFilter()方法: 

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    process(request.getRemoteHost(), request, response, chain);
}

protected void process(String property, ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    if (isAllowed(property)) { // 请求被允许,继续访问FilterChain上的下一个Filter
        chain.doFilter(request, response);
    } else {
        // 请求不被允许,则返回携带错误信息的响应
    }
}

也就是说,FilterChain的doFilter()方法和filter的doFilter()方法,通过相互调用形成了一条处理请求/响应对的责任链

FilterChain的doFilter()方法中的return语句

return语句的作用:避免多次调用servlet.service()方法

  • 场景一: FilterChain上的filter均成功执行
    • 从上面的分析可知,如果所有的filter都成功执行,最终将会将会访问到FilterChain上的最后一个filter
    • 此时,最后一个filter的doFilter()方法将调用chain.doFilter()方法,发现pos < n条件不满足,则if语句之外的diamante,即调用servlet.service()方法
    • servlet.service()方法执行完成后,从最后一个filter的doFilter()方法退出
    • 此时,如果没有return语句,会再次执行servlet.service()方法
  • 场景二: filter处理请求失败
    • 如果filter处理请求/响应对时失败,例如,发现客户端IP不是受信任的(RemoteHostFilter),发现请求体格式不对(FailedRequestFilter)
    • 此时,都将直接返回携带错误信息的响应,最终会从filter的doFilter()方法中返回
    • 如果不存在return语句,则会继续调用servlet.service()方法处理请求
    • 其实,这样的情况下,根本无需调用servlet.service()方法处理请求
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值