How to Streamline Contract Signing Using Strategy, Chain‑of‑Responsibility, and Annotations
This article walks through a Java Spring‑Boot implementation of a contract‑signing workflow, explains the original chain‑of‑responsibility design, shows its limitations, and demonstrates how to replace manual bean wiring with a strategy‑based annotation and enum configuration for flexible node management.
Background
The contract‑signing system originally follows a Chain‑of‑Responsibility (CoR) pattern. Each processing step is a node that receives the output of the previous node and passes its result to the next. The workflow consists of eight logical steps: contract text initialization, contract generation, shield handling, MQ sending, flow update, file upload, channel selection, and the actual channel invocation.
Original CoR Implementation
The project resides under DesignPatterns/src/main/java/com.xbhog.chainresponsibility. It defines request/response POJOs, a Processor interface, and a series of interceptor implementations such as ContractSignCompactInitImpl, ContractSignGenerateImpl, etc.
public class ContractRequest { private String name; private String age; private String status; }
public class ContractResponse { private String status; private String mas; } ContractSignProcessormanually injects each interceptor bean and builds a list:
List<Interceptor<T, ContractResponse>> interceptorList = new ArrayList<>();
interceptorList.add(contractCompactInitImpl);
interceptorList.add(contractGenerateImpl);
// ... other beans ...
return new ContractCall(paramter, interceptorList).exectue(); ContractCallcreates a ContractChain that recursively invokes interceptor.process(nextChain) until the last node is reached.
Problems with Manual Wiring
Adding or removing a node requires both a bean injection field change and a list‑construction modification, which is error‑prone and hampers extensibility.
Strategy‑Based Refactoring
To automate bean registration, a custom annotation @ContractSign, an enum ContractSignEnum.SignChannel, and a configuration class SignConfig are introduced. SignConfig scans the Spring application context for beans annotated with @ContractSign and stores them in a ConcurrentHashMap<Integer, Interceptor> keyed by the enum code.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContractSign { ContractSignEnum.SignChannel SIGN_CHANNEL(); } 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; }
public Integer getCode() { return code; }
public String getInfo() { return info; }
}
} 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 sign = AnnotationUtils.findAnnotation(interceptor.getClass(), ContractSign.class);
if (sign != null) {
CONTRACT_SIGN_MAP.put(sign.SIGN_CHANNEL().getCode(), interceptor);
}
});
}
}Each interceptor implementation is now annotated, for example:
@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
@Component
public class ContractSignCompactInitImpl<T extends ContractRequest> implements Interceptor<T, ContractResponse> {
@Override
public ContractResponse process(Chain<T, ContractResponse> chain) {
log.info("=============执行合同文本初始化拦截器开始");
T request = chain.request();
request.setStatus("1");
ContractResponse response = chain.proceed(request);
return response != null ? response : ContractResponse.builder().status("fail").mas("处理失败").build();
}
}The processor now extends SignConfig and builds the interceptor list by iterating over the sorted map keys, eliminating manual ordering:
public class ContractSignProcessor<T extends ContractRequest> extends SignConfig implements Processor<T, ContractResponse> {
@Override
public ContractResponse process(T paramter) {
List<Interceptor<T, ContractResponse>> interceptorList = new ArrayList<>();
for (Integer key : CONTRACT_SIGN_MAP.keySet()) {
interceptorList.add(CONTRACT_SIGN_MAP.get(key));
}
log.info("签章开始");
return new ContractCall(paramter, interceptorList).exectue();
}
}Result
With the annotation‑driven strategy, adding a new processing node only requires creating the implementation class and annotating it with the appropriate SIGN_CHANNEL. The framework automatically registers the bean and inserts it into the correct position based on the enum order, making the contract‑signing pipeline more maintainable and extensible.
Key Resources
GitHub repository: https://github.com/xbhog/DesignPatternsStudy
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
