1. 引言
本文将探讨一种广泛应用于Java开发中的行为型设计模式:责任链模式。
更多设计模式可参考我们的设计模式系列文章。
2. 责任链模式
根据Wikipedia的定义,责任链模式由"命令对象源和一系列处理对象"组成。
链中的每个处理对象负责特定类型的命令,处理完成后将命令转发给链中的下一个处理器。
责任链模式特别适用于以下场景:
- 解耦命令的发送者和接收者
- 在运行时动态选择处理策略
下面我们通过一个简单示例来理解这个模式。
3. 示例代码
我们将使用责任链模式创建一个处理认证请求的链。其中,认证提供者作为命令,每个认证处理器作为独立的处理对象。
首先创建处理器的抽象基类:
public abstract class AuthenticationProcessor {
public AuthenticationProcessor nextProcessor;
// 标准构造函数
public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}
接下来创建两个具体处理器,继承自AuthenticationProcessor:
public class OAuthProcessor extends AuthenticationProcessor {
public OAuthProcessor(AuthenticationProcessor nextProcessor) {
super(nextProcessor);
}
@Override
public boolean isAuthorized(AuthenticationProvider authProvider) {
if (authProvider instanceof OAuthTokenProvider) {
return true;
} else if (nextProcessor != null) {
return nextProcessor.isAuthorized(authProvider);
}
return false;
}
}
public class UsernamePasswordProcessor extends AuthenticationProcessor {
public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) {
super(nextProcessor);
}
@Override
public boolean isAuthorized(AuthenticationProvider authProvider) {
if (authProvider instanceof UsernamePasswordProvider) {
return true;
} else if (nextProcessor != null) {
return nextProcessor.isAuthorized(authProvider);
}
return false;
}
}
这里我们创建了两个具体处理器:UsernamePasswordProcessor和OAuthProcessor,每个都重写了isAuthorized方法。
现在编写测试用例:
public class ChainOfResponsibilityTest {
private static AuthenticationProcessor getChainOfAuthProcessor() {
AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null);
return new UsernamePasswordProcessor(oAuthProcessor);
}
@Test
public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() {
AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider()));
}
@Test
public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() {
AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider()));
}
}
上述示例构建了处理器链:UsernamePasswordProcessor -> OAuthProcessor。第一个测试通过,第二个测试失败。
处理流程:
- UsernamePasswordProcessor首先检查认证提供者是否为UsernamePasswordProvider实例
- 如果不是,则委托给OAuthProcessor
- OAuthProcessor处理命令:
- 第一个测试匹配成功
- 第二个测试没有匹配的处理器,导致认证失败
4. 实现原则
实现责任链模式时需牢记以下关键原则:
- 每个处理器需实现自己的命令处理逻辑
- 示例中所有处理器都实现了isAuthorized方法
- 每个处理器必须持有下一个处理器的引用
- 示例中UsernamePasswordProcessor委托给OAuthProcessor
- 处理器必须负责调用下一个处理器,避免命令丢失
- ⚠️ 警告:当命令是SamlProvider实例时,请求可能未被处理导致认证失败
- 处理器不能形成递归循环
- 示例链为:UsernamePasswordProcessor -> OAuthProcessor
- ❌ 如果将UsernamePasswordProcessor设置为OAuthProcessor的下一个处理器,会形成循环:UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor
- 每个命令只能由一个处理器处理
- 示例中当命令包含OAuthTokenProvider实例时,仅由OAuthProcessor处理
5. 实际应用
在Java生态中,我们每天都在使用责任链模式。最经典的例子是Java中的Servlet过滤器,它允许多个过滤器处理HTTP请求。不过在该场景中,每个过滤器调用的是过滤器链而非直接调用下一个过滤器。
看下面Servlet过滤器的代码片段:
public class CustomFilter implements Filter {
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// 处理请求
// 将请求(即命令)传递给过滤器链中的下一个处理器
chain.doFilter(request, response);
}
}
如代码所示,必须调用FilterChain的doFilter方法才能将请求传递给链中的下一个处理器。
6. 缺点分析
尽管责任链模式很实用,但也要注意其缺点:
- 容易破坏链结构:
- 处理器未调用下一个处理器会导致命令丢失
- 处理器调用错误处理器可能造成循环
- 可能产生深层堆栈,影响性能
- 处理器间代码重复,增加维护成本
7. 总结
本文通过一个认证请求处理链的示例,深入探讨了责任链模式的实现原理、优缺点及实际应用场景。
完整源代码可在GitHub仓库获取。