前置内容

  1. 把握战略形式
  2. 把握职责链形式
  3. 把握类承继、接口的完结
  4. 把握参数的传递与设置
  5. GitHub地址

ps:【文章由来】 公司项目中所用的合同签章处理流程,本人根据职责链上运用战略形式进行优化。

签章的处理流程

  1. 合同文本初始化
  2. 合同文本生成
  3. 签章挡板是否敞开
  4. 合同签章发送mq
  5. 合同签章流水更新
  6. 合同上传文件服务器
  7. 签章途径挑选
  8. 签章途径的实际调用

履行的流程如下:

【进阶玩法】策略+责任链+组合实现合同签章

整个结构类似于递归调用。每个节点中依靠上一个节点的输入以及下一个节点的输出,在中心进程能够完结每个节点的自界说操作,比较灵敏。

流程完结

GitHub地址

项目结构

DesignPatterns
└── src
  └── main
    └── java
      └── com.xbhog.chainresponsibility
                ├── annotations
        │  └── ContractSign
                ├── channel
                │  ├── ContractSignChannelImpl.java
        │  └── ContractSignChannel
                ├── Config
        │  └── SignConfig
                ├── Enum
        │  └── ContractSignEnum
        ├── impl
        │  ├── ContractSignCompactInitImpl.java
        │  ├── ContractSignGenerateImpl.java
                │  ├── ContractSignMockImpl.java  
        │  ├── ContractSignMqImpl.java
        │  ├── ContractSignSaveUploadImpl.java
        │  ├── ContractSignSerialImpl.java
        │  └── ContractSignTradeImpl.java
        ├── inter
        │  ├── Call
        │  ├── Chain
        │  ├── Interceptor
        │  └── Processor
        ├── pojo
        │  ├── ContractRequest.java
        │  └── ContractResponse.java
                ├── ContractCall
                ├── ContractChain
        └── ContractSignProcessor.java

项目类图

【进阶玩法】策略+责任链+组合实现合同签章

职责链+组合形式代码完结

工程结构

DesignPatterns
└── src
  └── main
    └── java
      └── com.xbhog.chainresponsibility
                ├── channel
                │  ├── ContractSignChannelImpl.java
        │  └── ContractSignChannel
        ├── impl
        │  ├── ContractSignCompactInitImpl.java
        │  ├── ContractSignGenerateImpl.java
                │  ├── ContractSignMockImpl.java  
        │  ├── ContractSignMqImpl.java
        │  ├── ContractSignSaveUploadImpl.java
        │  ├── ContractSignSerialImpl.java
        │  └── ContractSignTradeImpl.java
        ├── inter
        │  ├── Call
        │  ├── Chain
        │  ├── Interceptor
        │  └── Processor
        ├── pojo
        │  ├── ContractRequest.java
        │  └── ContractResponse.java
                ├── ContractCall
                ├── ContractChain
        └── ContractSignProcessor.java

职责链中的目标界说

//恳求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContractRequest {
​
  private String name;
​
  private String age;
​
  private String status;
}
//呼应
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContractResponse {
  private String status;
​
  private String mas;
}

界说流程中的恳求及呼应类,便利处理每个职责链的恳求、回来信息。

职责链处理流程

/**
 * @author xbhog
 * @describe: 职责链+组合完结合同签章
 * @date 2023/7/11
 */
@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequest> implements Processor<T, ContractResponse> {
​
  @Resource(name = "contractSignCompactInitImpl")
  private Interceptor<T,ContractResponse> contractCompactInitImpl;
    ......
​
​
  public ContractSignProcessor() {
   }
​
  @Override
  public ContractResponse process(T paramter) {
    //获取一切的监听器
    List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
    interceptorList.add(contractCompactInitImpl);
     ......
    //开端签章
    log.info("签章开端");
    return new ContractCall(paramter,interceptorList).exectue();
   }
}
​

合同签章办法的主流程调用接口(进口) ,该类中注入一切的节点完结类(如contractCompactInitImpl),经过编排完结职责链流程。 在初始化节点之前,进行节点的封装以及数据恳求的处理。例:contractCompactInitImpl-合同数据初始化节点

/**
 * @author xbhog
 * @describe: 合同数据恳求、节点的实例化及办法履行
 * @date 2023/7/11
 */
public class ContractCall<T extends ContractRequest> implements Call<T, ContractResponse> {
  private final T originalRequest;
  private final List<Interceptor<T,ContractRequest>> interceptorList;
​
  public ContractCall(T originalRequest, List<Interceptor<T, ContractRequest>> interceptorList) {
    this.originalRequest = originalRequest;
    this.interceptorList = interceptorList;
   }
​
  @Override
  public T request() {
    return this.originalRequest;
   }
​
  @Override
  public ContractResponse exectue() {
    //实例化流程节点
    ContractChain<T> chain = new ContractChain(0,this.originalRequest,this.interceptorList);
    return chain.proceed(this.originalRequest);
   }
}

获取节点中的恳求参数,实例化当时职责链节点(contractCompactInitImpl),在履行节点中的proceed办法来获取当时节点的参数以及获取节点的信息。

/**
 * @author xbhog
 * @describe: 合同节点
 * @date 2023/7/11
 */
@Slf4j
public class ContractChain<T extends ContractRequest> implements Chain<T, ContractResponse> {
  private final Integer index;
​
  private final T request;
​
  private final List<Interceptor<T,ContractResponse>> interceptors;
​
  public ContractChain(Integer index, T request, List<Interceptor<T, ContractResponse>> interceptors) {
    this.index = index;
    this.request = request;
    this.interceptors = interceptors;
   }
​
  @Override
  public T request() {
    return this.request;
   }
​
  @Override
  public ContractResponse proceed(T request) {
    //操控节点流程
    if(this.index >= this.interceptors.size()){
      throw new IllegalArgumentException("index越界");
     }
    //下一个节点参数设置
    Chain<T,ContractResponse> nextChain = new ContractChain(this.index + 1, request, this.interceptors);
    //获取节点信息
    Interceptor<T, ContractResponse> interceptor = this.interceptors.get(this.index);
    Class<? extends Interceptor> aClass = interceptor.getClass();
    log.info("当时节点:{}",aClass.getSimpleName());
    ContractResponse response = interceptor.process(nextChain);
    if(Objects.isNull(response)){
      throw new NullPointerException("intercetor"+interceptor+"return null");
     }
    return response;
   }
}
​

到此合同签章的架构流程已经确定,后续只需填充Interceptor具体的完结类即可。 在代码中ContractResponse response = interceptor.process(nextChain);来履行合同初始化节点的具体操作。

/**
 * @author xbhog
 * @describe: 合同文本初始化
 * @date 2023/7/12
 */
@Slf4j
@Component
public class ContractSignCompactInitImpl<T extends ContractRequest> implements Interceptor<T, ContractResponse> {
  public ContractSignCompactInitImpl() {
   }
​
  @Override
  public ContractResponse process(Chain<T,ContractResponse> chain) {
    log.info("=============履行合同文本初始化拦截器开端");
    //获取处理的恳求参数
    T request = chain.request();
    request.setStatus("1");
    log.info("=============履行合同文本初始化拦截器完毕");
    //进入下一个职责链节点
    ContractResponse response = chain.proceed(request);
    if(Objects.isNull(response)){
      log.error("回来值的为空");
      response = ContractResponse.builder().status("fail").mas("处理失败").build();
     }
    //其他处理
    return response;
   }
}
​

测试验证

@SpringBootTest
class SPringBootTestApplicationTests {
  @Autowired
  @Qualifier("contractSignProcessor")
  private Processor<ContractRequest,ContractResponse> contractSignProcessor;
​
  @Test
  void contextLoads() {
    ContractRequest contractRequest = new ContractRequest();
    contractRequest.setName("xbhog");
    contractRequest.setAge("12");
    ContractResponse process = contractSignProcessor.process(contractRequest);
    System.out.println(process);
   }
​
}

在这里只需要调用合同签章进口的办法即可进入合同签章的流程。

2023-07-16 13:25:13.063  INFO 26892 --- [           main] c.e.s.c.ContractSignProcessor            : 签章开端
2023-07-16 13:25:13.067  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignCompactInitImpl
2023-07-16 13:25:13.068  INFO 26892 --- [           main] c.e.s.c.i.ContractSignCompactInitImpl    : =============履行合同文本初始化拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.i.ContractSignCompactInitImpl    : =============履行合同文本初始化拦截器完毕
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignGenerateImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignGenerateImpl    : =============履行合同文本生成拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignGenerateImpl    : =============履行合同文本生成拦截器完毕
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignMockImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMockImpl        : =============履行签章挡板拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMockImpl        : =============履行签章挡板拦截器完毕
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignMqImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : =============履行合同签章完结发送mq拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignSerialImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSerialImpl      : =============履行合同签章流水处理拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignSaveUploadImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : =============履行合同签章完结上传服务器拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当时节点:ContractSignTradeImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignTradeImpl       : =============履行签章途径实际调用拦截器开端
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.channel.ContractSignChannelImpl  : 签章处理开端
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : 开端上传服务器
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : .............
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : 上传服务器完结
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : =============履行合同签章完结上传服务器拦截器完毕
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSerialImpl      : =============履行合同签章流水处理拦截器完毕
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : 发送MQ给下流处理数据
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : =============履行合同签章完结发送mq拦截器完毕
ContractResponse(status=success, mas=处理成功)

战略+职责链+组合代码完结

以下是完整的合同签章进口完结类:

/**
 * @author xbhog
 * @describe: 职责链+组合完结合同签章
 * @date 2023/7/11
 */
@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequest> implements Processor<T, ContractResponse> {
    @Resource(name = "contractSignCompactInitImpl")
    private Interceptor<T,ContractResponse> contractCompactInitImpl;
    @Resource(name = "contractSignGenerateImpl")
    private Interceptor<T,ContractResponse> contractGenerateImpl;
    @Resource(name = "contractSignMockImpl")
    private Interceptor<T,ContractResponse> contractSignMockImpl;
    @Resource(name = "contractSignMqImpl")
    private Interceptor<T,ContractResponse> contractSignMqImpl;
    @Resource(name = "contractSignSaveUploadImpl")
    private Interceptor<T,ContractResponse> contractSignSaveUploadImpl;
    @Resource(name = "contractSignSerialImpl")
    private Interceptor<T,ContractResponse> contractSignSerialImpl;
    @Resource(name = "contractSignTradeImpl")
    private Interceptor<T,ContractResponse> ContractSignTradeImpl;
    public ContractSignProcessor() {
    }
    @Override
    public ContractResponse process(T paramter) {
        //获取一切的监听器
        List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
        interceptorList.add(contractCompactInitImpl);
        interceptorList.add(contractGenerateImpl);
        interceptorList.add(contractSignMockImpl);
        interceptorList.add(contractSignMqImpl);
        interceptorList.add(contractSignSerialImpl);
        interceptorList.add(contractSignSaveUploadImpl);
        interceptorList.add(ContractSignTradeImpl);
        //开端签章
        log.info("签章开端");
        return new ContractCall(paramter,interceptorList).exectue();
    }
}

能够看到,现在的合同签章的处理流程需要的节点数已经7个了,后续如果新增节点或许削减节点都需要对该类进行手动的处理;比方:削减一个节点的流程。

  1. 删去节点完结的注入
  2. 删去list中的bean完结类

为便利后续的拓展(懒是社会进步的加速器,不是),在职责链,组合的基础上经过战略形式来修改bean的注入方法。 完整的项目结构和项目类图就是作者文章开端放的,可回来查看。 在榜首部分的基础上添加的功用点如下

  1. 新增签章注解
  2. 新增签章节点枚举
  3. 新增签章装备类

签章注解完结

package com.example.springboottest.chainresponsibility.annotations;
import com.example.springboottest.chainresponsibility.Enum.ContractSignEnum;
import java.lang.annotation.*;
/**
 * @author xbhog
 * @describe:
 * @date 2023/7/15
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContractSign {
    ContractSignEnum.SignChannel SIGN_CHANNEL();
}

设置注解润饰目标的范围,主要是对bean的一个注入,所以类型挑选type,

  • TYPE: 用于描绘类、接口(包括注解类型) 或enum声明

设置注解的运行周期(有用范围),一般是运行时有用,

  • RUNTIME:在运行时有用 (大部分注解的挑选)

设置该注解的数据类型,

枚举完结

package com.xbhog.chainresponsibility.Enum;
/**
 * @author xbhog
 * @describe:
 * @date 2023/7/15
 */
public class ContractSignEnum {
    public enum SignChannel {
        SIGN_INIT(1, "合同文本初始化"),
        SIGN_GENERATE(2, "合同文本生成"),
        SIGN_MOCK(3, "签章挡板"),
        SIGN_MQ(4, "合同签章完结发送MQ"),
        SIGN_TABLE(5, "合同签章表处理"),
        SIGN_UPLOAD(6, "合同签章完结上传服务器"),
        SIGN_TRADE(7, "签章途径实际调用");
        private Integer code;
        private String info;
        SignChannel(Integer code, String info) {
            this.code = code;
            this.info = info;
        }
        ......
    }
}

对合同签章中的流程节点进行统一的装备。

签章装备类

在项目发动的时分,经过注解东西类AnnotationUtils扫描一切被ContractSign注解润饰的类,将这些类经过Map进行存储,便利后续的调用。

public class SignConfig {
    @Resource
    protected List<Interceptor> contractSignList;
    protected static final Map<Integer,Interceptor> CONTRACT_SIGN_MAP = new ConcurrentHashMap<>();
    @PostConstruct
    public void init(){
       contractSignList.forEach(interceptor -> {
           //查找这个接口的完结类上有没有ContractSign注解
           ContractSign sign = AnnotationUtils.findAnnotation(interceptor.getClass(), ContractSign.class);
           if(!Objects.isNull(sign)){
               CONTRACT_SIGN_MAP.put(sign.SIGN_CHANNEL().getCode(),interceptor);
           }
       });
    }
}

到此,简化了Bean的注入方法。

签章注解运用

以合同文本初始化ContractSignCompactInitImpl来说。

/**
 * @author xbhog
 * @describe: 合同文本初始化
 * @date 2023/7/12
 */
@Slf4j
@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
@Component
public class ContractSignCompactInitImpl<T extends ContractRequest> implements Interceptor<T, ContractResponse> {
    public ContractSignCompactInitImpl() {
    }
    @Override
    public ContractResponse process(Chain<T,ContractResponse> chain) {
        log.info("=============履行合同文本初始化拦截器开端");
        //获取处理的恳求参数
        T request = chain.request();
        request.setStatus("1");
        log.info("=============履行合同文本初始化拦截器完毕");
        //进入下一个职责链节点
        ContractResponse response =  chain.proceed(request);
        if(Objects.isNull(response)){
            log.error("回来值的为空");
            response = ContractResponse.builder().status("fail").mas("处理失败").build();
        }
        //其他处理
        return response;
    }
}

在该完结类上绑定了枚举@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT). 在合同签章进口类( **ContractSignProcessor** )中的改变如下:

@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequest> extends SignConfig implements Processor<T, ContractResponse> {
    public ContractSignProcessor() {
    }
    @Override
    public ContractResponse process(T paramter) {
        //获取一切的监听器
        List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
        //获取排序后的成果,确保职责链的次序,hashmap中key如果是数字的话,经过hashcode编码后是有序的
        for(Integer key : CONTRACT_SIGN_MAP.keySet()){
            interceptorList.add(CONTRACT_SIGN_MAP.get(key));
        }
        //开端签章
        log.info("签章开端");
        return new ContractCall(paramter,interceptorList).exectue();
    }
}

经过承继合同签章装备类(SignConfig),来获取Map,遍历Map添加到list后进入职责链流程。 到此,整个战略+职责链+组合的优化方法完毕了。


问题: 职责链中的次序是怎样确保的? 信任认真看完的你能在文章或许代码中找到答案。