责任链模式
责任链模式(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之后
缺点
责任链模式是一种有用的设计模式,但也有一些缺点需要考虑:
性能问题: 当责任链中的处理器较多时,可能会影响性能。因为请求必须依次经过所有的处理器,即使某个处理器无法处理请求。这可能会导致一些不必要的开销,特别是在处理大量请求的情况下。
请求丢失风险: 如果责任链没有正确配置或处理器未能正确传递请求,请求可能会被丢失或不被处理。这需要确保责任链的每个处理器都正确实现了处理逻辑和链的传递。
调试和维护复杂性: 在大型责任链中,调试和维护可能变得复杂。理解请求的处理流程以及找到问题的源头可能需要花费更多的时间。因此,需要仔细设计和文档化责任链,以便更容易进行调试和维护。
不适合所有情况: 责任链模式适用于处理请求的场景,但不是所有问题都适合使用这种模式。在某些情况下,可能有更简单和有效的解决方案,不需要引入责任链的复杂性。
可能导致循环链: 如果责任链的配置不当,可能会导致循环链的问题,即某个处理器在链中被多次调用,导致无限循环。这需要谨慎设计和配置责任链以避免这种情况。
虽然责任链模式有一些缺点,但在合适的情况下,它仍然是一种有用的设计模式,可以提高代码的可维护性和可扩展性。为了最大程度地减轻这些缺点,应该仔细考虑责任链的设计和配置,并进行充分的测试和验证。
总结
责任链模式是一种有助于解耦请求发送者和接收者的设计模式。它通过将多个处理者连接成一个链,依次尝试处理请求,从而实现请求的分发和处理。这种模式使得系统更具弹性,能够轻松添加新的处理者和更改处理顺序,从而提高了代码的可扩展性和可维护性。