责任链设计模式与应用:实现拦截器责任链统计接口执行时间和性能

时间:2022-11-23 08:55:50


一、什么是责任链设计模式

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