一、什么是责任链设计模式
1、什么是责任链设计模式?
责任链设计模式主要构成有抽象处理者、具体处理者、客户类,在处理请求的时候,将请求通过客户类发送至处理链路上,这样链路上所有处理对象都有机会处理请求,使发送者与接收者之间解耦。
2、使用场景
使用责任链模式的优点:
- 发送者与接收方的处理对象类之间解耦;
- 封装每个处理对象,符合类的最小封装原则;
- 可以任意添加处理对象,调整处理对象之间的顺序,提高了维护性和可拓展性;
使用场景:
当请求到来时,不知道由哪个具体对象去处理或者每个对象都需要处理请求的时候,可以使用责任链模式。比如在OA系统中,当员工发起一个流程的时候,需要经过HR-组长-部门老大-公司老总等审批的时候,就可以使用责任链模式,不同的处理对象(HR-组长-部门老大-公司老总)使用不同的封装类组装成一个处理链路,使用这个处理链路去处理员工发起的请求。
3、责任链模式与策略模式的区别
策略模式和责任链模式很像,都是有多个处理对象去处理同一个请求。
不同之处在于对于同一个请求,策略模式可以经过选择使用具体的策略类处理请求;而责任链模式不能根据请求判断使用哪个处理类处理请求,需要链路上的处理类全部处理一遍请求才能得出结果。
二、代码实战
代码:https://github.com/nomico271/inspire-demo/tree/master/Ch1_ExecutorChainPattern
下面将结合Spring AOP知识,应用责任链设计模式,设计一个能够打印调用接口的参数、统计执行时间、执行次数的拦截器。
用到的知识点有:
- 框架:Spring Boot
- 设计模式:责任链设计模式
- Spring AOP
- 反射
- 注解
项目结构:
示意图如下:
下面看下代码实现。
1、拦截器责任链的实现
(1)首先定义拦截器抽象:可以定义拦截器的顺序、方法执行前的逻辑和方法执行完的逻辑,见InspireInterceptor.java;
(2)实现自定义功能的具体拦截器的实现,如打印接口入参信息的拦截器、统计接口调用执行时间的拦截器、统计接口调用次数的拦截器等等,见:
- CostTimeInterceptor.java
- LoggerInterceptor.java
- MethodCountInterceptor.java
(3)拦截器客户端,构建拦截器执行链并进行排序,之后按顺序执行拦截器链路中拦截器的before和after方法,见InspireInterceptorChainClient.java
对责任链进行排序的代码如下:
@PostConstruct
public void loadInterceptors() {
if (!CollectionUtils.isEmpty(interceptorList)) {
for (InspireInterceptor interceptor : interceptorList) {
interceptor.setOrder(resolveOrder(interceptor));
}
Collections.sort(interceptorList, (o1, o2) -> o1.getOrder() - o2.getOrder());
}
}
/**
* 获取拦截器注解中定义的优先级
*
* @param interceptor
* @return
*/
private int resolveOrder(InspireInterceptor interceptor) {
if (!interceptor.getClass().isAnnotationPresent(InterceptorOrder.class)) {
return InterceptorOrder.LOWEST_ORDER;
} else {
return interceptor.getClass().getAnnotation(InterceptorOrder.class).order();
}
}
(4)另:在拦截器执行过程中,可以自定义拦截器执行顺序,见注解@InterceptorOrder;
同时,需要有个上下文保存调用的方法获取接口入参等信息,以便能够在多个拦截器中传递,见:InspireContext、InspireRequest、InspireResponse、TheadLocalHolder
完整代码如下:
// 注解类, 定义拦截器顺序
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface InterceptorOrder {
/**
* 优先级, 值越小, 优先级越高
*
* @return
*/
int order() default LOWEST_ORDER ;
int LOWEST_ORDER = Integer.MAX_VALUE;
int HIGHEST_ORDER = Integer.MIN_VALUE;
}
// 拦截器
public abstract class InspireInterceptor {
private int order;
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public boolean executeBefore(InspireContext context) {
return true;
}
public void executeAfter(InspireContext context) {}
}
// 拦截器实现1: 打印接口调用参数
@Slf4j
@Component
@InterceptorOrder(order = 10)
public class LoggerInterceptor extends InspireInterceptor {
@Override
public boolean executeBefore(InspireContext context) {
return super.executeBefore(context);
}
@Override
public void executeAfter(InspireContext context) {
String method = context.getRequest().getMethod();
Map<String, Object> paramsMap = context.getRequest().getParamsMap();
log.info("invoke [method:{}] start, params:{}", method, paramsMap.toString());
}
}
// 拦截器实现2 - 统计接口调用次数
@Slf4j
@Component
@InterceptorOrder(order = 20)
public class MethodCountInterceptor extends InspireInterceptor {
// 统计方法调用次数
private static final Map<String, Integer> methodCountMap = new ConcurrentHashMap<>();
// 统计方法调用次数
private static final Map<String, Integer> methodSuccessCountMap = new ConcurrentHashMap<>();
// 统计方法调用失败次数
private static final Map<String, Integer> methodFailCountMap = new ConcurrentHashMap<>();
@Override
public boolean executeBefore(InspireContext context) {
return true;
}
@Override
public void executeAfter(InspireContext context) {
log.info("[MethodCountInterceptor] execute after");
RpcResult result = (RpcResult) context.getResponse().getData();
String method = context.getRequest().getMethod();
setMethodCount(method, methodCountMap);
if (result.isSuccess()) {
setMethodCount(method, methodSuccessCountMap);
} else {
setMethodCount(method, methodFailCountMap);
}
log.info("invoke method:[{}], success times:{}, fail times:{}, total times:{}", method,
getMethodCount(method, methodSuccessCountMap), getMethodCount(method, methodFailCountMap), getMethodCount(method, methodCountMap));
}
private void setMethodCount(String key, final Map<String, Integer> map) {
if (map.get(key) == null) {
map.put(key, 1);
} else {
map.put(key, map.get(key) + 1);
}
}
private int getMethodCount(String key, Map<String, Integer> map) {
if (map.get(key) == null) {
return 0;
}
return map.get(key);
}
}
// // 拦截器实现3 - 统计接口执行时间
@Slf4j
@Component
@InterceptorOrder(order = 30)
public class CostTimeInterceptor extends InspireInterceptor {
private long start;
private long end;
@Override
public boolean executeBefore(InspireContext context) {
start = System.currentTimeMillis();
return true;
}
@Override
public void executeAfter(InspireContext context) {
end = System.currentTimeMillis();
log.info("invoke method:{} costTime:{} ms, result:{}", context.getRequest().getMethod(), (end - start), context.getResponse().getData());
}
}
// 拦截器客户端:初始化拦截器建成拦截器责任链,对拦截器进行排序,并按顺序依次执行拦截器链
@Component
public class InspireInterceptorChainClient {
@Autowired
private List<InspireInterceptor> interceptorList;
@PostConstruct
public void loadInterceptors() {
if (!CollectionUtils.isEmpty(interceptorList)) {
for (InspireInterceptor interceptor : interceptorList) {
interceptor.setOrder(resolveOrder(interceptor));
}
Collections.sort(interceptorList, (o1, o2) -> o1.getOrder() - o2.getOrder());
}
}
/**
* 获取拦截器注解中定义的优先级
*
* @param interceptor
* @return
*/
private int resolveOrder(InspireInterceptor interceptor) {
if (!interceptor.getClass().isAnnotationPresent(InterceptorOrder.class)) {
return InterceptorOrder.LOWEST_ORDER;
} else {
return interceptor.getClass().getAnnotation(InterceptorOrder.class).order();
}
}
public boolean processBefore(InspireContext context) {
for (InspireInterceptor interceptor : interceptorList) {
boolean isPass = interceptor.executeBefore(context);
if (!isPass) {
return isPass;
}
}
return true;
}
public void processAfter(InspireContext context) {
for (InspireInterceptor interceptor : interceptorList) {
interceptor.executeAfter(context);
}
}
}
// 上下文类:InspireContext、InspireRequest、InspireResponse、TheadLocalHolder
// 拦截器上下文类:InspireContext
@Data
public final class InspireContext {
private InspireRequest request;
private InspireResponse response;
public InspireContext(InspireRequest request, InspireResponse response) {
this.request = request;
this.response = response;
}
public static void setContext(InspireContext context) {
TheadLocalHolder.setInspireContext(context);
}
public static InspireContext getContext() {
return TheadLocalHolder.getInspireContext();
}
public static void removeContext() {
TheadLocalHolder.removeInspireContext();
}
}
// 方法请求参数
@Data
public class InspireRequest {
private String method;
private Map<String, Object> paramsMap;
}
// 方法执行结果
@Data
public class InspireResponse<T> {
private T data;
}
// 保存当前线程信息
public class TheadLocalHolder {
private static final ThreadLocal<InspireContext> LOCAL_CONTEXT= new ThreadLocal() ;
public static void setInspireContext(InspireContext context){
LOCAL_CONTEXT.set(context) ;
}
public static void removeInspireContext(){
LOCAL_CONTEXT.remove();
}
public static InspireContext getInspireContext(){
return LOCAL_CONTEXT.get() ;
}
}
2、切面类:在调用接口方法前后执行拦截器
完整代码如下:
@Component
@Aspect
public class MethodAspect {
@Autowired
private InspireInterceptorChainClient interceptorClient;
@Pointcut("execution(* com.wgs.inspire.设计模式.demo2.责任链.test.service.*.*(..))")
public void doLogPointCut(){}
@Around("doLogPointCut()")
public Object doAfterReturning(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("====invoke aop start====");
// build inspireContext
InspireContext inspireContext = buildInspireContext(joinPoint);
// execute interceptorChain
interceptorClient.processBefore(inspireContext);
RpcResult result = (RpcResult) joinPoint.proceed();
inspireContext.getResponse().setData(result);
interceptorClient.processAfter(inspireContext);
System.out.println("====invoke aop end====");
return result;
}
private InspireContext buildInspireContext(ProceedingJoinPoint joinPoint) {
InspireRequest request = new InspireRequest();
request.setMethod(joinPoint.getSignature().getName());
Map<String, Object> map = new HashMap<>();
// 参数名
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] paramNames = methodSignature.getParameterNames();
// 参数对应的值
Object[] args = joinPoint.getArgs();
for (int i = 0; i < paramNames.length; i++) {
map.put(paramNames[i], args[i]);
}
request.setParamsMap(map);
InspireResponse response = new InspireResponse();
InspireContext inspireContext = new InspireContext(request, response);
return inspireContext;
}
}
需要配置AOP的使用:
application.properties:
spring.aop.auto=true
3、测试部分
注意:为了在切面类中获取接口调用结果统一,需要定义好接口返回值的包装类,此处用的是RpcResult以及对应构建器RpcResultBuilder。
测试的完整代码如下:
// 为了方便解析接口请求结果,使用RpcResult统一返回结果
@Data
public class RpcResult<T> implements Serializable{
private String code;
private boolean success;
private String msg;
private T data;
}
public class RpcResultBuilder {
public static <T> RpcResult<T> buildSuccess(T data) {
RpcResult<T> result = new RpcResult();
result.setSuccess(Boolean.TRUE);
result.setCode("200");
result.setMsg("success");
result.setData(data);
return result;
}
public static <T> RpcResult<T> buildError(String code, String errorMsg) {
RpcResult<T> result = new RpcResult();
result.setSuccess(Boolean.FALSE);
result.setCode(code);
result.setMsg(errorMsg);
return result;
}
}
// 接口
public interface RefundService {
RpcResult<String> refund(String order, String operator);
RpcResult<Boolean> refuseRefund(String orderId, String reason);
}
@Service
public class RefundServiceImpl implements RefundService {
@Override
public RpcResult<String> refund(String order, String operator) {
try {
// 模拟接口执行时间
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Refund order: " + order);
return RpcResultBuilder.buildSuccess("success");
} catch (Exception e) {
return RpcResultBuilder.buildError("400", "false");
}
}
@Override
public RpcResult<Boolean> refuseRefund(String orderId, String reason) {
try {
// 模拟接口执行时间
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 模拟出错
int orderNum = Integer.valueOf(orderId);
if (orderNum == 0) {
int mockError = orderNum / 0 ;
}
System.out.println("Refuse Refund order: " + orderId + ", reason : " + reason);
return RpcResultBuilder.buildSuccess(true);
} catch (Exception e) {
return RpcResultBuilder.buildError("400", "false");
}
}
// 测试类
@Controller
public class TestController {
@Autowired
private RefundService refundService;
@RequestMapping("/test/refund")
@ResponseBody
public void testService(@RequestParam("orderId") String order,
@RequestParam("operator")String operator) {
refundService.refund(order, operator);
}
@RequestMapping("/test/refuseRefund")
@ResponseBody
public void testRefuseRefundService(@RequestParam("orderId") String order,
@RequestParam("reason")String reason) {
refundService.refuseRefund(order, reason);
}
}
4、测试结果如下
注:以上代码未在生产环境中使用,仅提供一个思路,部分代码还需要斟酌修改。
参考:https://crossoverjie.top/2018/10/22/wheel/cicada5/
代码:https://github.com/nomico271/inspire-demo/tree/master/Ch1_ExecutorChainPattern