如何在Spring中优雅的使用责任链模式

王大爷 2022年05月26日 732次浏览

背景:

收到需求需要校验用户身份证和姓名是否匹配,要求可以顺序执行不同的校验方法,任一步返回结果,很容易想到责任链模式,网上的例子多半是原理讲解类的,没有充分使用 Spring 特点,所以记录一下。技术有限,如有纰漏,欢迎指正。

  1. 要求增加新的处理渠道不改动原来的代码。

  2. 使用 Spring 的方式编写。

重点:

  • Spring 支持一次性将一个接口的所有实现类一次性载入进来
  • 借用 @Order 注解 实现责任链的先后排序
  • 使用 @Bean 指定名称初始化

首先,准备一个抽象类,重要的有三点,可以指定的下一个处理对象,执行控制方法,和实际校验方法。

@Data
public abstract class HandlerChain {

    private HandlerChain **nextHandler**;
    /**
     \*   执行控制方法
     \* @param person
     \* @return
     \*/
    public CheckResultDTO  handler(PersonDTO person){
        CheckResultDTO result = check(person);
        
	if (!result.getIsOver()) {
         return  **nextHandler**.handler(person);        
	}
        return result;    
	}
    /**
     \*  实际校验逻辑
     \* @param person
     \* @return
     \*/
    public abstract CheckResultDTO  check(PersonDTO person);
}


然后是三个实现类,@Order (0) 注解可以控制实例加载的顺序,参数越小越优先。以下均为伪代码,具体逻辑看业务需求。

@Order(0)
@Component
public class DataBaseHandler extends HandlerChain {
    @Override    
public CheckResultDTO check(PersonDTO person) {
        //查询数据库         略。。。        
boolean checkResult = false;        
if (checkResult) {
            return new CheckResultDTO(false,"校验未通过",true);        
}
        return new CheckResultDTO(false,"继续验证",false);    }
}
@Order(1)
@Component
public class AliHandler extends HandlerChain {
    @Override    
   public CheckResultDTO check(PersonDTO person) {
        //调用阿里的PIA        略。。。 
       
    boolean checkResult = false;   
     
   if (checkResult) 
   {
     return new CheckResultDTO(false,"校验未通过",true);        
   }
   return new CheckResultDTO(false,"继续验证",false);    
}
}
@Order(2)
@Component
public class ThirdHandler extends HandlerChain {
    @Override    
    public CheckResultDTO check(PersonDTO person) {
        //查询第三方的API        略。。。        
     boolean checkResult = true;        
     if (checkResult) {
            return new CheckResultDTO(false,"校验通过",true);        
	}
        return new CheckResultDTO(false,"未知信息",true);    		}
}

最后是链条组装初始化的配置:

@Configuration
public class InitHandlerChain {
    @Autowired    
    List  **handlerChains**;
    @Bean(name = "firstHandler")
    public HandlerChain  firstHandler(){
        int size = **handlerChains**.size();        
       for (int i=0;i

其中通过 @Autowird 注入的 list 将 HandlerChain 的所有子类都注入进来,且是按照 @Order 注解规定的顺序。

调用方式:

@RestController
public class CheckController {

    @Autowired    
   @Qualifier("firstHandler")
    private HandlerChain  **firstHandler**;
    @RequestMapping("/check")
    public CheckResultDTO check(PersonDTO personDTO){
        CheckResultDTO handler =**firstHandler**.handler(personDTO);        
return handler;    
}
}

由于 @Autowired 是按照类型注入的 所以需要配合 @Qualifier 指定名称使用 此处也可使用 @Resource。