经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
【进阶玩法】策略+责任链+组合实现合同签章
来源:cnblogs  作者:xbhog  时间:2023/7/17 9:38:22  对本文有异议

前置内容

  1. 掌握策略模式
  2. 掌握责任链模式
  3. 掌握类继承、接口的实现
  4. 掌握参数的传递与设置
  5. GitHub地址

ps:【文章由来】公司项目中所用的合同签章处理流程,本人基于责任链上使用策略模式进行优化。

签章的处理流程

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

执行的流程如下:

整个结构类似于递归调用。每个节点中依赖上一个节点的输入以及下一个节点的输出,在中间过程可以实现每个节点的自定义操作,比较灵活。

流程实现

GitHub地址

项目结构

  1. DesignPatterns
  2. └── src
  3. └── main
  4. └── java
  5. └── com.xbhog.chainresponsibility
  6. ├── annotations
  7. └── ContractSign
  8. ├── channel
  9. ├── ContractSignChannelImpl.java
  10. └── ContractSignChannel
  11. ├── Config
  12. └── SignConfig
  13. ├── Enum
  14. └── ContractSignEnum
  15. ├── impl
  16. ├── ContractSignCompactInitImpl.java
  17. ├── ContractSignGenerateImpl.java
  18. ├── ContractSignMockImpl.java
  19. ├── ContractSignMqImpl.java
  20. ├── ContractSignSaveUploadImpl.java
  21. ├── ContractSignSerialImpl.java
  22. └── ContractSignTradeImpl.java
  23. ├── inter
  24. ├── Call
  25. ├── Chain
  26. ├── Interceptor
  27. └── Processor
  28. ├── pojo
  29. ├── ContractRequest.java
  30. └── ContractResponse.java
  31. ├── ContractCall
  32. ├── ContractChain
  33. └── ContractSignProcessor.java

项目类图

责任链+组合模式代码实现

工程结构

  1. DesignPatterns
  2. └── src
  3. └── main
  4. └── java
  5. └── com.xbhog.chainresponsibility
  6. ├── channel
  7. ├── ContractSignChannelImpl.java
  8. └── ContractSignChannel
  9. ├── impl
  10. ├── ContractSignCompactInitImpl.java
  11. ├── ContractSignGenerateImpl.java
  12. ├── ContractSignMockImpl.java
  13. ├── ContractSignMqImpl.java
  14. ├── ContractSignSaveUploadImpl.java
  15. ├── ContractSignSerialImpl.java
  16. └── ContractSignTradeImpl.java
  17. ├── inter
  18. ├── Call
  19. ├── Chain
  20. ├── Interceptor
  21. └── Processor
  22. ├── pojo
  23. ├── ContractRequest.java
  24. └── ContractResponse.java
  25. ├── ContractCall
  26. ├── ContractChain
  27. └── ContractSignProcessor.java

责任链中的对象定义

  1. //请求
  2. @Data
  3. @NoArgsConstructor
  4. @AllArgsConstructor
  5. @Builder
  6. public class ContractRequest {
  7. private String name;
  8. private String age;
  9. private String status;
  10. }
  11. //响应
  12. @Data
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. @Builder
  16. public class ContractResponse {
  17. private String status;
  18. private String mas;
  19. }

定义流程中的请求及响应类,方便处理每个责任链的请求、返回信息。

责任链处理流程

  1. /**
  2. * @author xbhog
  3. * @describe: 责任链+组合实现合同签章
  4. * @date 2023/7/11
  5. */
  6. @Slf4j
  7. @Component
  8. public class ContractSignProcessor <T extends ContractRequest> implements Processor<T, ContractResponse> {
  9. @Resource(name = "contractSignCompactInitImpl")
  10. private Interceptor<T,ContractResponse> contractCompactInitImpl;
  11. ......
  12. public ContractSignProcessor() {
  13. }
  14. @Override
  15. public ContractResponse process(T paramter) {
  16. //获取所有的监听器
  17. List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
  18. interceptorList.add(contractCompactInitImpl);
  19. ......
  20. //开始签章
  21. log.info("签章开始");
  22. return new ContractCall(paramter,interceptorList).exectue();
  23. }
  24. }

合同签章方法的主流程调用接口(入口),该类中注入所有的节点实现类(如contractCompactInitImpl),通过编排实现责任链流程。
在初始化节点之前,进行节点的封装以及数据请求的处理。例:contractCompactInitImpl-合同数据初始化节点

  1. /**
  2. * @author xbhog
  3. * @describe: 合同数据请求、节点的实例化及方法执行
  4. * @date 2023/7/11
  5. */
  6. public class ContractCall<T extends ContractRequest> implements Call<T, ContractResponse> {
  7. private final T originalRequest;
  8. private final List<Interceptor<T,ContractRequest>> interceptorList;
  9. public ContractCall(T originalRequest, List<Interceptor<T, ContractRequest>> interceptorList) {
  10. this.originalRequest = originalRequest;
  11. this.interceptorList = interceptorList;
  12. }
  13. @Override
  14. public T request() {
  15. return this.originalRequest;
  16. }
  17. @Override
  18. public ContractResponse exectue() {
  19. //实例化流程节点
  20. ContractChain<T> chain = new ContractChain(0,this.originalRequest,this.interceptorList);
  21. return chain.proceed(this.originalRequest);
  22. }
  23. }

获取节点中的请求参数,实例化当前责任链节点(contractCompactInitImpl),在执行节点中的proceed方法来获取当前节点的参数以及获取节点的信息。

  1. /**
  2. * @author xbhog
  3. * @describe: 合同节点
  4. * @date 2023/7/11
  5. */
  6. @Slf4j
  7. public class ContractChain<T extends ContractRequest> implements Chain<T, ContractResponse> {
  8. private final Integer index;
  9. private final T request;
  10. private final List<Interceptor<T,ContractResponse>> interceptors;
  11. public ContractChain(Integer index, T request, List<Interceptor<T, ContractResponse>> interceptors) {
  12. this.index = index;
  13. this.request = request;
  14. this.interceptors = interceptors;
  15. }
  16. @Override
  17. public T request() {
  18. return this.request;
  19. }
  20. @Override
  21. public ContractResponse proceed(T request) {
  22. //控制节点流程
  23. if(this.index >= this.interceptors.size()){
  24. throw new IllegalArgumentException("index越界");
  25. }
  26. //下一个节点参数设置
  27. Chain<T,ContractResponse> nextChain = new ContractChain(this.index + 1, request, this.interceptors);
  28. //获取节点信息
  29. Interceptor<T, ContractResponse> interceptor = this.interceptors.get(this.index);
  30. Class<? extends Interceptor> aClass = interceptor.getClass();
  31. log.info("当前节点:{}",aClass.getSimpleName());
  32. ContractResponse response = interceptor.process(nextChain);
  33. if(Objects.isNull(response)){
  34. throw new NullPointerException("intercetor"+interceptor+"return null");
  35. }
  36. return response;
  37. }
  38. }

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

  1. /**
  2. * @author xbhog
  3. * @describe: 合同文本初始化
  4. * @date 2023/7/12
  5. */
  6. @Slf4j
  7. @Component
  8. public class ContractSignCompactInitImpl<T extends ContractRequest> implements Interceptor<T, ContractResponse> {
  9. public ContractSignCompactInitImpl() {
  10. }
  11. @Override
  12. public ContractResponse process(Chain<T,ContractResponse> chain) {
  13. log.info("=============执行合同文本初始化拦截器开始");
  14. //获取处理的请求参数
  15. T request = chain.request();
  16. request.setStatus("1");
  17. log.info("=============执行合同文本初始化拦截器结束");
  18. //进入下一个责任链节点
  19. ContractResponse response = chain.proceed(request);
  20. if(Objects.isNull(response)){
  21. log.error("返回值的为空");
  22. response = ContractResponse.builder().status("fail").mas("处理失败").build();
  23. }
  24. //其他处理
  25. return response;
  26. }
  27. }

测试验证

  1. @SpringBootTest
  2. class SPringBootTestApplicationTests {
  3. @Autowired
  4. @Qualifier("contractSignProcessor")
  5. private Processor<ContractRequest,ContractResponse> contractSignProcessor;
  6. @Test
  7. void contextLoads() {
  8. ContractRequest contractRequest = new ContractRequest();
  9. contractRequest.setName("xbhog");
  10. contractRequest.setAge("12");
  11. ContractResponse process = contractSignProcessor.process(contractRequest);
  12. System.out.println(process);
  13. }
  14. }

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

  1. 2023-07-16 13:25:13.063 INFO 26892 --- [ main] c.e.s.c.ContractSignProcessor : 签章开始
  2. 2023-07-16 13:25:13.067 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignCompactInitImpl
  3. 2023-07-16 13:25:13.068 INFO 26892 --- [ main] c.e.s.c.i.ContractSignCompactInitImpl : =============执行合同文本初始化拦截器开始
  4. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.i.ContractSignCompactInitImpl : =============执行合同文本初始化拦截器结束
  5. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignGenerateImpl
  6. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignGenerateImpl : =============执行合同文本生成拦截器开始
  7. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignGenerateImpl : =============执行合同文本生成拦截器结束
  8. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignMockImpl
  9. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMockImpl : =============执行签章挡板拦截器开始
  10. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMockImpl : =============执行签章挡板拦截器结束
  11. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignMqImpl
  12. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : =============执行合同签章完成发送mq拦截器开始
  13. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignSerialImpl
  14. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSerialImpl : =============执行合同签章流水处理拦截器开始
  15. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignSaveUploadImpl
  16. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : =============执行合同签章完成上传服务器拦截器开始
  17. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignTradeImpl
  18. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignTradeImpl : =============执行签章渠道实际调用拦截器开始
  19. 2023-07-16 13:25:13.069 INFO 26892 --- [ main] c.e.s.c.channel.ContractSignChannelImpl : 签章处理开始
  20. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : 开始上传服务器
  21. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : .............
  22. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : 上传服务器完成
  23. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : =============执行合同签章完成上传服务器拦截器结束
  24. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSerialImpl : =============执行合同签章流水处理拦截器结束
  25. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : 发送MQ给下游处理数据
  26. 2023-07-16 13:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : =============执行合同签章完成发送mq拦截器结束
  27. ContractResponse(status=success, mas=处理成功)

策略+责任链+组合代码实现

以下是完整的合同签章入口实现类:

  1. /**
  2. * @author xbhog
  3. * @describe: 责任链+组合实现合同签章
  4. * @date 2023/7/11
  5. */
  6. @Slf4j
  7. @Component
  8. public class ContractSignProcessor <T extends ContractRequest> implements Processor<T, ContractResponse> {
  9. @Resource(name = "contractSignCompactInitImpl")
  10. private Interceptor<T,ContractResponse> contractCompactInitImpl;
  11. @Resource(name = "contractSignGenerateImpl")
  12. private Interceptor<T,ContractResponse> contractGenerateImpl;
  13. @Resource(name = "contractSignMockImpl")
  14. private Interceptor<T,ContractResponse> contractSignMockImpl;
  15. @Resource(name = "contractSignMqImpl")
  16. private Interceptor<T,ContractResponse> contractSignMqImpl;
  17. @Resource(name = "contractSignSaveUploadImpl")
  18. private Interceptor<T,ContractResponse> contractSignSaveUploadImpl;
  19. @Resource(name = "contractSignSerialImpl")
  20. private Interceptor<T,ContractResponse> contractSignSerialImpl;
  21. @Resource(name = "contractSignTradeImpl")
  22. private Interceptor<T,ContractResponse> ContractSignTradeImpl;
  23. public ContractSignProcessor() {
  24. }
  25. @Override
  26. public ContractResponse process(T paramter) {
  27. //获取所有的监听器
  28. List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
  29. interceptorList.add(contractCompactInitImpl);
  30. interceptorList.add(contractGenerateImpl);
  31. interceptorList.add(contractSignMockImpl);
  32. interceptorList.add(contractSignMqImpl);
  33. interceptorList.add(contractSignSerialImpl);
  34. interceptorList.add(contractSignSaveUploadImpl);
  35. interceptorList.add(ContractSignTradeImpl);
  36. //开始签章
  37. log.info("签章开始");
  38. return new ContractCall(paramter,interceptorList).exectue();
  39. }
  40. }

可以看到,目前的合同签章的处理流程需要的节点数已经7个了,后续如果新增节点或者减少节点都需要对该类进行手动的处理;比如:减少一个节点的流程。

  1. 删除节点实现的注入
  2. 删除list中的bean实现类

为方便后续的拓展(懒是社会进步的加速器,不是),在责任链,组合的基础上通过策略模式来修改bean的注入方式。
完整的项目结构和项目类图就是作者文章开始放的,可返回查看。
在第一部分的基础上增加的功能点如下

  1. 新增签章注解
  2. 新增签章节点枚举
  3. 新增签章配置类

签章注解实现

  1. package com.example.springboottest.chainresponsibility.annotations;
  2. import com.example.springboottest.chainresponsibility.Enum.ContractSignEnum;
  3. import java.lang.annotation.*;
  4. /**
  5. * @author xbhog
  6. * @describe:
  7. * @date 2023/7/15
  8. */
  9. @Target(ElementType.TYPE)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. public @interface ContractSign {
  12. ContractSignEnum.SignChannel SIGN_CHANNEL();
  13. }

设置注解修饰对象的范围,主要是对bean的一个注入,所以类型选择type,

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

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

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

设置该注解的数据类型,

  • ENUM:枚举类型,方便统一处理

枚举实现

  1. package com.xbhog.chainresponsibility.Enum;
  2. /**
  3. * @author xbhog
  4. * @describe:
  5. * @date 2023/7/15
  6. */
  7. public class ContractSignEnum {
  8. public enum SignChannel {
  9. SIGN_INIT(1, "合同文本初始化"),
  10. SIGN_GENERATE(2, "合同文本生成"),
  11. SIGN_MOCK(3, "签章挡板"),
  12. SIGN_MQ(4, "合同签章完成发送MQ"),
  13. SIGN_TABLE(5, "合同签章表处理"),
  14. SIGN_UPLOAD(6, "合同签章完成上传服务器"),
  15. SIGN_TRADE(7, "签章渠道实际调用");
  16. private Integer code;
  17. private String info;
  18. SignChannel(Integer code, String info) {
  19. this.code = code;
  20. this.info = info;
  21. }
  22. ......
  23. }
  24. }

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

签章配置类

在项目启动的时候,通过注解工具类AnnotationUtils扫描所有被ContractSign注解修饰的类,将这些类通过Map进行存储,方便后续的调用。

  1. public class SignConfig {
  2. @Resource
  3. protected List<Interceptor> contractSignList;
  4. protected static final Map<Integer,Interceptor> CONTRACT_SIGN_MAP = new ConcurrentHashMap<>();
  5. @PostConstruct
  6. public void init(){
  7. contractSignList.forEach(interceptor -> {
  8. //查找这个接口的实现类上有没有ContractSign注解
  9. ContractSign sign = AnnotationUtils.findAnnotation(interceptor.getClass(), ContractSign.class);
  10. if(!Objects.isNull(sign)){
  11. CONTRACT_SIGN_MAP.put(sign.SIGN_CHANNEL().getCode(),interceptor);
  12. }
  13. });
  14. }
  15. }

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

签章注解使用

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

  1. /**
  2. * @author xbhog
  3. * @describe: 合同文本初始化
  4. * @date 2023/7/12
  5. */
  6. @Slf4j
  7. @ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
  8. @Component
  9. public class ContractSignCompactInitImpl<T extends ContractRequest> implements Interceptor<T, ContractResponse> {
  10. public ContractSignCompactInitImpl() {
  11. }
  12. @Override
  13. public ContractResponse process(Chain<T,ContractResponse> chain) {
  14. log.info("=============执行合同文本初始化拦截器开始");
  15. //获取处理的请求参数
  16. T request = chain.request();
  17. request.setStatus("1");
  18. log.info("=============执行合同文本初始化拦截器结束");
  19. //进入下一个责任链节点
  20. ContractResponse response = chain.proceed(request);
  21. if(Objects.isNull(response)){
  22. log.error("返回值的为空");
  23. response = ContractResponse.builder().status("fail").mas("处理失败").build();
  24. }
  25. //其他处理
  26. return response;
  27. }
  28. }

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

  1. @Slf4j
  2. @Component
  3. public class ContractSignProcessor <T extends ContractRequest> extends SignConfig implements Processor<T, ContractResponse> {
  4. public ContractSignProcessor() {
  5. }
  6. @Override
  7. public ContractResponse process(T paramter) {
  8. //获取所有的监听器
  9. List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
  10. //获取排序后的结果,保证责任链的顺序,hashmap中key如果是数字的话,通过hashcode编码后是有序的
  11. for(Integer key : CONTRACT_SIGN_MAP.keySet()){
  12. interceptorList.add(CONTRACT_SIGN_MAP.get(key));
  13. }
  14. //开始签章
  15. log.info("签章开始");
  16. return new ContractCall(paramter,interceptorList).exectue();
  17. }
  18. }

通过继承合同签章配置类(SignConfig),来获取Map,遍历Map添加到list后进入责任链流程。
到此,整个策略+责任链+组合的优化方式结束了。


问题:
责任链中的顺序是怎么保证的?
相信认真看完的你能在文章或者代码中找到答案。

原文链接:https://www.cnblogs.com/xbhog/p/17559095.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号