责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,属于行为型模式。
一、概述 责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,属于行为型模式。下面放一张足球比赛的图,通过层层传递,最终射门。通过这张图,可以更好的理解责任链模式。
二、入门案例 2.1 类图
2.2 基础类介绍 抽象接口RequestHandler
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 13:41
* @description
*/
public interface RequestHandler {
void doHandler( String req) ;
}
抽象类BaseRequestHandler
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 13:45
* @description
*/
public abstract class BaseRequestHandler implements RequestHandler {
protected RequestHandler next;
public void next( RequestHandler next) {
this.next = next;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 具体处理类AHandler
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 14:00
* @description
*/
public class AHandler extends BaseRequestHandler {
@Override
public void doHandler( String req) {
// 处理自己的业务逻辑
System.out .println ( "A中处理自己的逻辑" ) ;
// 传递给下个类(若链路中还有下个处理类)
if ( next != null ) {
next.doHandler ( req) ;
}
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 当然还有具体的处理类B、C等等,这里不展开赘述。
使用类Client
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 14:06
* @description
*/
public class Client {
public static void main( String[ ] args) {
BaseRequestHandler a = new AHandler( ) ;
BaseRequestHandler b = new BHandler( ) ;
BaseRequestHandler c = new CHandler( ) ;
a.next ( b) ;
b.next ( c) ;
a.doHandler ( "链路待处理的数据" ) ;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 2.3 处理流程图
三、应用场景 3.1 场景举例 场景一
前两年,在一家金融公司待过一段时间,其中就有一个业务场景:一笔订单进来,会先在后台通过初审人员进行审批,初审不通过,订单流程结束。初审通过以后,会转给终审人员进行审批,不通过,流程结束;通过,流转到下个业务场景。对于这块业务代码,之前一代目是一个叫知了的同事,他撸起袖子就是干,一套if-else干到底。后来,技术老大CodeReview,点名要求改掉这块。于是乎,想到用用设计模式吧,然后就噼里啪啦一顿改。(当然,比较复杂的情况,还是可以用工作流来处理这个场景,当时碍于时间成本,也就放弃了)。
场景二
上家公司对接甲方爸爸的时候,对方会调用我们接口,将数据同步过来。同样,我们需要将处理好的数据,传给他们。由于双方传输数据都是加密传输,所以在接受他们数据之前,需要对数据进行解密,验签,参数校验等操作。同样,我们给他们传数据也需要进行加签,加密操作。
具体案例
话不多说,对于场景二,我来放一些伪代码,跟大家一起探讨下。1、一切从注解开始,我这里自定义了一个注解@Duty,这个注解有spring的@Component注解,也就是标记了这个自定义注解的类,都是交给spring的bean容器去管理。注解中,有两个属性:1.type,定义相同的type类型的bean,会被放到一个责任链集合中。2.order,同一个责任链集合中,bean的排序,数值越小,会放到链路最先的位置,优先处理。
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 16:11
* @description
*/
@Target( { ElementType.TYPE } )
@Retention( RetentionPolicy.RUNTIME )
@Documented
@Inherited
@Service
public @interface Duty {
/**
* 标记具体业务场景
* @return
*/
String type( ) default "" ;
/**
* 排序:数值越小,排序越前
* @return
*/
int order ( ) default 0 ;
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 2、定义一个顶层的抽象接口IHandler,传入2个泛型参数,供后续自定义。
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 15:31
* @description 责任链顶层抽象类
*/
public interface IHandler< T, R> {
/**
* 抽象处理类
* @param t
* @return
*/
R handle( T t) ;
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 3、定义一个责任链bean的管理类HandleChainManager,用来存放不同业务下的责任链路集合。在该类中,有一个Map和两个方法。
handleMap:这个map会存放责任链路中,具体的执行类,key是注解@Duty中定义的type值,value是标记了@Duty注解的bean集合,也就是具体的执行类集合。
setHandleMap:传入具体执行bean的集合,存放在map中。
executeHandle:从map中找到具体的执行bean集合,并依次执行。
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 16:00
* @description 责任链管理类
*/
public class HandleChainManager {
/**
* 存放责任链路上的具体处理类
* k-具体业务场景名称
* v-具体业务场景下的责任链路集合
*/
private Map< String, List< IHandler>> handleMap;
/**
* 存放系统中责任链具体处理类
* @param handlerList
*/
public void setHandleMap( List< IHandler> handlerList) {
handleMap = handlerList
.stream ( )
.sorted ( Comparator.comparingInt ( h -> AnnotationUtils.findAnnotation ( h.getClass ( ) , Duty.class ) .order ( ) ) )
.collect ( Collectors.groupingBy ( handler -> AnnotationUtils.findAnnotation ( handler.getClass ( ) , Duty.class ) .type ( ) ) ) ;
}
/**
* 执行具体业务场景中的责任链集合
* @param type 对应@Duty注解中的type,可以定义为具体业务场景
* @param t 被执行的参数
*/
public < T, R> R executeHandle( String type, T t) {
List< IHandler> handlers = handleMap.get ( type) ;
R r = null ;
if ( CollectionUtil.isNotEmpty ( handlers) ) {
for ( IHandler< T, R> handler : handlers) {
r = handler.handle ( t) ;
}
}
return r;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 4、定义一个配置类PatternConfiguration,用于装配上面的责任链管理器HandleChainManager。
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 15:35
* @description 设计模式配置类
*/
@Configuration
public class PatternConfiguration {
@Bean
public HandleChainManager handlerChainExecute( List< IHandler> handlers) {
HandleChainManager handleChainManager = new HandleChainManager( ) ;
handleChainManager.setHandleMap ( handlers) ;
return handleChainManager;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 5、具体的处理类:SignChainHandler、EncryptionChainHandler、RequestChainHandler,这里我以SignChainHandler为例。在具体处理类上标记自定义注解@Duty,该类会被注入到bean容器中,实现IHandler接口,只需关心自己的handle方法,处理具体的业务逻辑。
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/10/25 15:31
* @description 加签类
*/
@Duty( type = BusinessConstants.REQUEST , order = 1 )
public class SignChainHandler implements IHandler< String, String> {
/**
* 处理加签逻辑
* @param s
* @return
*/
@Override
public String handle( String s) {
// 加签逻辑
System.out .println ( "甲方爸爸要求加签" ) ;
return "加签" ;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 6、具体怎么调用?这里我写了个测试controller直接调用,具体如下:
复制 /**
* @author 往事如风
* @version 1.0
* @date 2022/9/6 17:32
* @description
*/
@RestController
@Slf4j
public class TestController {
@Resource
private HandleChainManager handleChainManager;
@PostMapping( "/send" )
public String duty( @RequestBody String requestBody) {
String response = handleChainManager.executeHandle ( BusinessConstants.REQUEST , requestBody) ;
return response;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 7、执行结果,会按照注解中标记的order依次执行。
至此,完工。又可以开心的撸代码了,然后在具体的执行类中,又是一顿if-else。。。
四、源码中运用 4.1Mybatis源码中的运用 Mybatis中的缓存接口Cache,cache作为一个缓存接口,最主要的功能就是添加和获取缓存的功能,作为接口它有11个实现类,分别实现不同的功能,下面是接口源码和实现类。
复制 package org.apache .ibatis .cache ;
import java.util .concurrent .locks .ReadWriteLock ;
public interface Cache {
String getId( ) ;
void putObject( Object var1, Object var2) ;
Object getObject( Object var1) ;
Object removeObject( Object var1) ;
void clear( ) ;
int getSize( ) ;
default ReadWriteLock getReadWriteLock( ) {
return null ;
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
下面,我们来看下其中一个子类LoggingCache的源码。主要看他的putObject方法和getObject方法,它在方法中直接传给下一个实现去执行。这个实现类其实是为了在获取缓存的时候打印缓存的命中率的。
复制 public class LoggingCache implements Cache {
private final Log log;
private final Cache delegate;
protected int requests = 0 ;
protected int hits = 0 ;
public LoggingCache( Cache delegate) {
this.delegate = delegate;
this.log = LogFactory.getLog ( this.getId ( ) ) ;
}
// ...
public void putObject( Object key, Object object) {
this.delegate .putObject ( key, object) ;
}
public Object getObject( Object key) {
++ this.requests ;
Object value = this.delegate .getObject ( key) ;
if ( value != null ) {
++ this.hits ;
}
if ( this.log .isDebugEnabled ( ) ) {
this.log .debug ( "Cache Hit Ratio [" + this.getId ( ) + "]: " + this.getHitRatio ( ) ) ;
}
return value;
}
// ...
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 最后,经过Cache接口各种实现类的处理,最终会到达PerpetualCache这个实现类。与之前的处理类不同的是,这个类中有一个map,在map中做存取,也就是说,最终缓存还是会保存在map中的。
复制 public class PerpetualCache implements Cache {
private final String id;
private final Map< Object, Object> cache = new HashMap( ) ;
public PerpetualCache( String id) {
this.id = id;
}
// ...
public void putObject( Object key, Object value) {
this.cache .put ( key, value) ;
}
public Object getObject( Object key) {
return this.cache .get ( key) ;
}
// ...
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 4.2spring源码中的运用 4.2.1DispatcherServlet类
DispatcherServlet 核心方法 doDispatch。HandlerExecutionChain只是维护HandlerInterceptor的集合,可以向其中注册相应的拦截器,本身不直接处理请求,将请求分配给责任链上注册处理器执行,降低职责链本身与处理逻辑之间的耦合程度。
复制 protected void doDispatch( HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null ;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager ( request) ;
try {
ModelAndView mv = null ;
Exception dispatchException = null ;
try {
processedRequest = checkMultipart( request) ;
multipartRequestParsed = ( processedRequest != request) ;
// Determine handler for the current request.
mappedHandler = getHandler( processedRequest) ;
if ( mappedHandler == null ) {
noHandlerFound( processedRequest, response) ;
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter( mappedHandler.getHandler ( ) ) ;
// Process last- modified header, if supported by the handler.
String method = request.getMethod ( ) ;
boolean isGet = "GET" .equals ( method) ;
if ( isGet || "HEAD" .equals ( method) ) {
long lastModified = ha.getLastModified ( request, mappedHandler.getHandler ( ) ) ;
if ( new ServletWebRequest( request, response) .checkNotModified ( lastModified) && isGet) {
return;
}
}
if ( ! mappedHandler.applyPreHandle ( processedRequest, response) ) {
return;
}
// Actually invoke the handler.
mv = ha.handle ( processedRequest, response, mappedHandler.getHandler ( ) ) ;
if ( asyncManager.isConcurrentHandlingStarted ( ) ) {
return;
}
applyDefaultViewName( processedRequest, mv) ;
mappedHandler.applyPostHandle ( processedRequest, response, mv) ;
}
catch ( Exception ex) {
dispatchException = ex;
}
catch ( Throwable err) {
// As of 4.3 , we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 4.2.2HandlerExecutionChain类
这里分析的几个方法,都是从DispatcherServlet类的doDispatch方法中请求的。
复制 boolean applyPreHandle( HttpServletRequest request,
HttpServletResponse response) throws Exception {
HandlerInterceptor[ ] interceptors = this.getInterceptors ( ) ;
if ( ! ObjectUtils.isEmpty ( interceptors) ) {
for( int i = 0 ; i < interceptors.length ; this.interceptorIndex = i++ ) {
HandlerInterceptor interceptor = interceptors[ i] ;
if ( ! interceptor.preHandle ( request, response, this.handler ) ) {
this.triggerAfterCompletion ( request, response, ( Exception) null ) ;
return false ;
}
}
}
return true ;
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 在applyPreHandle方法中,执行triggerAfterCompletion方法
复制 void triggerAfterCompletion( HttpServletRequest request,
HttpServletResponse response, Exception ex) throws Exception {
HandlerInterceptor[ ] interceptors = this.getInterceptors ( ) ;
if ( ! ObjectUtils.isEmpty ( interceptors) ) {
for( int i = this.interceptorIndex ; i >= 0 ; --i) {
HandlerInterceptor interceptor = interceptors[ i] ;
try {
interceptor.afterCompletion ( request, response, this.handler , ex) ;
} catch ( Throwable var8) {
logger.error ( "HandlerInterceptor.afterCompletion threw exception" , var8) ;
}
}
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 获取拦截器,执行applyPostHandle方法
复制 void applyPostHandle( HttpServletRequest request,
HttpServletResponse response, ModelAndView mv)
throws Exception {
HandlerInterceptor[ ] interceptors = this.getInterceptors ( ) ;
if ( ! ObjectUtils.isEmpty ( interceptors) ) {
for( int i = interceptors.length - 1 ; i >= 0 ; --i) {
HandlerInterceptor interceptor = interceptors[ i] ;
interceptor.postHandle ( request, response, this.handler , mv) ;
}
}
}
五、总结 5.1 优点 将请求与处理解耦。 请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,转发给下一个节点。 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。 链路结构灵活,可以通过改变链路的结构动态的新增或删减责任。 易于扩展新的请求处理类(节点),符合开闭原则。 5.2 缺点 责任链太长或者处理时间过长,会影响整体性能。
如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。
六、参考源码 编程文档:
https://gitee.com/cicadasmile/butte-java-note
应用仓库:
https://gitee.com/cicadasmile/butte-flyer-parent