责任链模式

责任链模式(Chain of Responsibility Pattern)

责任链模式是一种行为型设计模式,它允许你将请求发送者与多个请求处理者解耦,并将它们连接成一个处理链。 当有请求发生时,请求会沿着这个链传递,直到有一个处理者处理它为止。这有助于减少发送者和接收者之间的耦合,使系统更加灵活和可扩展。

概念

责任链模式由以下角色组成:

抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将请求转给它的后继者。

客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,客户类不关心处理细节和请求的传递过程。

代码

需求1:a1 -> a2 -> a3
实现先a1处理完交给a2,a2处理完再交给a3的逻辑,即: a1 -> a2 -> a3

定义处理者类

public class Teacher {

    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    // 下一个处理的老师;1、链条的引用点
    private Teacher next;

    public Teacher getNext() {
        return next;
    }

    public void setNext(Teacher next) {
        this.next = next;
    }

    void handlerRequest() {
        System.out.println(this + "正在处理。。。。。。。。");
        // 2、下一个继续
        if (next != null) {
            next.handlerRequest();
        }
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + ''' +
                '}';
    }
}


创建责任链并触发处理

public class MainTest {
    public static void main(String[] args) {
        Teacher a1 = new Teacher("a1");
        Teacher a2 = new Teacher("a2");
        Teacher a3 = new Teacher("a3");

        // 3、构造链条
        // a1->a2->a3
        a1.setNext(a2);
        a2.setNext(a3);

        a1.handlerRequest();

    }
}

测试结果

Teacher{name='a1'}正在处理。。。。。。。。
Teacher{name='a2'}正在处理。。。。。。。。
Teacher{name='a3'}正在处理。。。。。。。。

应用

在Spring框架中,责任链模式被广泛应用在不同的功能模块中。以下是一些Spring框架中使用责任链模式的示例:

Spring Security(Spring安全框架): Spring Security是Spring框架的一个模块,用于处理身份验证和授权。它使用责任链模式来处理各种安全过滤器,例如认证、授权、CSRF(跨站请求伪造)保护等。每个过滤器都可以看作是一个处理者,它们依次处理安全相关的任务,例如身份验证、权限检查等。

Spring AOP(面向切面编程): Spring AOP模块使用责任链模式来处理切面(Aspect)和通知(Advice)。在面向切面编程中,切面可以被看作是责任链中的一个处理者,它在特定的连接点(join point)上执行通知。多个切面可以按照指定的顺序组成一个切面链,以便依次执行通知。

Spring Web框架(如Spring MVC): 在Spring Web框架中,拦截器(Interceptor)可以被视为责任链中的处理者。拦截器用于在请求处理过程中执行预处理和后处理操作。你可以配置多个拦截器,并定义它们的执行顺序,从而创建一个拦截器链。

Spring Boot中的过滤器(Filter): 在Spring Boot应用中,可以使用Servlet过滤器来处理HTTP请求和响应。这些过滤器可以按照配置的顺序创建一个责任链,以便在请求处理过程中依次执行。

Spring Cloud中的过滤器(Zuul、Gateway): 在微服务架构中,Spring Cloud的一些组件(例如Zuul和Spring Cloud Gateway)允许你定义过滤器链,用于在请求到达微服务之前或离开微服务之后执行各种操作,如路由、鉴权、日志记录等。

这些是Spring框架中一些使用责任链模式的示例。责任链模式有助于解耦不同的功能模块,提高了代码的可扩展性和可维护性,因此在Spring中的不同模块中都可以找到其应用。实际上,Spring框架本身就是一个由许多协同工作的组件构成的责任链,每个组件负责不同的任务,例如依赖注入、事务管理、AOP等。

下面我们根据filter做示例

1->2->3->my.hello->3->2->1
代码实现:
1、 Filter 接口

public interface Filter {
    void doFilter(Request request,Response response,FilterChain chain);
}
@Data
public class Request {
    // 请求内容
    String msg;

    public Request(String msg) {
        this.msg = msg;
    }
}


@Data
public class Response {

    // 响应内容
    private String content;

    public Response(String content) {
        this.content = content;
    }
}

2、Filter 的三个实现类
在这里插入图片描述

public class HttpFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        // 第一个filter的功能
        request.msg += ">>>";

        System.out.println("HttpFilter ... doFilter之前");
        // 放行
        chain.doFilter(request, response, chain);
        System.out.println("HttpFilter ... doFilter之后");
    }
}

public class CharacterFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        // 功能
        request.msg += "====";

        System.out.println("CharacterFilter ... doFilter之前");
        // 放行
        chain.doFilter(request, response, chain);
        System.out.println("CharacterFilter ... doFilter之后");
    }
}

public class EncodingFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        // 功能
        request.msg += "oooo";

        System.out.println("EncodingFilter ... doFilter之前");
        // 放行
        chain.doFilter(request, response, chain);
        System.out.println("EncodingFilter ... doFilter之后");
    }
}

3、我们的目标方法

public class My {
    void hello() {

        System.out.println("调用my.hello()方法");
    }
}

4、维护链条

/**
 * 靠他维护链条
 * handlerExecutionChain
 *
 * @since 2023/8/13 8:19
 */
public class FilterChain implements Filter {

    // 游标:记录当前执行的步骤
    int cursor;

    // Filter 的链
    List<Filter> filterChain = new ArrayList<>();

    // 最终要执行的目标方法
    My target;

    // 添加 filter 方法
    void addFilter(Filter filter) {
        filterChain.add(filter);
    }

    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        // 执行第一个 filter ,一次往下

        // 游标小于总数量filter一直往下获取执行
        if (cursor < filterChain.size()) {
            Filter filter = filterChain.get(cursor);
            cursor++;
            // 执行 filter
            filter.doFilter(request, response, chain);
        } else {
            // filter执行完了,需要执行目标方法了
            target.hello();
        }
    }

    public My getTarget() {
        return target;
    }

    public void setTarget(My target) {
        this.target = target;
    }
}


5、调用主方法

public class MainTest {
    public static void main(String[] args) {
        FilterChain chain = new FilterChain();

        // web.xml
        HttpFilter httpFilter = new HttpFilter();
        CharacterFilter characterFilter = new CharacterFilter();
        EncodingFilter encodingFilter = new EncodingFilter();
        chain.addFilter(httpFilter);
        chain.addFilter(characterFilter);
        chain.addFilter(encodingFilter);

        chain.setTarget(new My());


        // filter 如何链式执行
        chain.doFilter(new Request("hello,world"), new Response("dddddddddddddddddd"), chain);
    }
}

6、执行结果

HttpFilter ... doFilter之前
CharacterFilter ... doFilter之前
EncodingFilter ... doFilter之前
调用my.hello()方法
EncodingFilter ... doFilter之后
CharacterFilter ... doFilter之后
HttpFilter ... doFilter之后


缺点

责任链模式是一种有用的设计模式,但也有一些缺点需要考虑:

性能问题: 当责任链中的处理器较多时,可能会影响性能。因为请求必须依次经过所有的处理器,即使某个处理器无法处理请求。这可能会导致一些不必要的开销,特别是在处理大量请求的情况下。

请求丢失风险: 如果责任链没有正确配置或处理器未能正确传递请求,请求可能会被丢失或不被处理。这需要确保责任链的每个处理器都正确实现了处理逻辑和链的传递。

调试和维护复杂性: 在大型责任链中,调试和维护可能变得复杂。理解请求的处理流程以及找到问题的源头可能需要花费更多的时间。因此,需要仔细设计和文档化责任链,以便更容易进行调试和维护。

不适合所有情况: 责任链模式适用于处理请求的场景,但不是所有问题都适合使用这种模式。在某些情况下,可能有更简单和有效的解决方案,不需要引入责任链的复杂性。

可能导致循环链: 如果责任链的配置不当,可能会导致循环链的问题,即某个处理器在链中被多次调用,导致无限循环。这需要谨慎设计和配置责任链以避免这种情况。

虽然责任链模式有一些缺点,但在合适的情况下,它仍然是一种有用的设计模式,可以提高代码的可维护性和可扩展性。为了最大程度地减轻这些缺点,应该仔细考虑责任链的设计和配置,并进行充分的测试和验证。

总结

责任链模式是一种有助于解耦请求发送者和接收者的设计模式。它通过将多个处理者连接成一个链,依次尝试处理请求,从而实现请求的分发和处理。这种模式使得系统更具弹性,能够轻松添加新的处理者和更改处理顺序,从而提高了代码的可扩展性和可维护性。