Implementation of Contract Signing Process Using Chain of Responsibility, Strategy, and Composite Patterns in Java
This article explains a Java-based contract signing system that combines the Chain of Responsibility, Composite, and Strategy design patterns within a Spring Boot application, detailing the processing flow, project structure, key code implementations, and how annotations and enums simplify bean injection.
Signature Processing Flow
Contract text initialization
Contract text generation
Check whether the signature shield is enabled
Send contract signature via MQ
Update contract signature transaction log
Upload contract file to file server
Select signature channel
Actual invocation of the signature channel
The execution follows a recursive‑call style where each node depends on the output of the previous node and provides a flexible hook for custom operations.
Process Implementation
https://github.com/xbhog/DesignPatternsStudy
Project Structure
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.javaProject Class Diagram
Chain of Responsibility + Composite Pattern Code Implementation
Request and Response Objects
@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;
}The above classes encapsulate the data passed through each chain node.
Chain Processor
@Slf4j
@Component
public class ContractSignProcessor
implements Processor
{
@Resource(name = "contractCompactInitImpl")
private Interceptor
contractCompactInitImpl;
// other interceptor fields omitted for brevity
@Override
public ContractResponse process(T paramter) {
List
> interceptorList = new ArrayList<>();
interceptorList.add(contractCompactInitImpl);
// add other interceptors …
log.info("签章开始");
return new ContractCall(paramter, interceptorList).exectue();
}
}ContractCall – Entry Point of the Chain
public class ContractCall
implements Call
{
private final T originalRequest;
private final List
> interceptorList;
public ContractCall(T originalRequest, List
> interceptorList) {
this.originalRequest = originalRequest;
this.interceptorList = interceptorList;
}
@Override
public T request() { return originalRequest; }
@Override
public ContractResponse exectue() {
ContractChain
chain = new ContractChain<>(0, this.originalRequest, this.interceptorList);
return chain.proceed(this.originalRequest);
}
}ContractChain – Controls Node Traversal
public class ContractChain
implements Chain
{
private final Integer index;
private final T request;
private final List
> interceptors;
public ContractChain(Integer index, T request, List
> interceptors) {
this.index = index;
this.request = request;
this.interceptors = interceptors;
}
@Override
public T request() { return request; }
@Override
public ContractResponse proceed(T request) {
if (this.index >= this.interceptors.size()) {
throw new IllegalArgumentException("index越界");
}
Chain
nextChain = new ContractChain<>(this.index + 1, request, this.interceptors);
Interceptor
interceptor = this.interceptors.get(this.index);
Class
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;
}
}Sample Interceptor – Contract Text Initialization
@Slf4j
@Component
public class ContractSignCompactInitImpl
implements Interceptor
{
@Override
public ContractResponse process(Chain
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;
}
}Test Verification
@SpringBootTest
class SPringBootTestApplicationTests {
@Autowired
@Qualifier("contractSignProcessor")
private Processor
contractSignProcessor;
@Test
void contextLoads() {
ContractRequest contractRequest = new ContractRequest();
contractRequest.setName("xbhog");
contractRequest.setAge("12");
ContractResponse process = contractSignProcessor.process(contractRequest);
System.out.println(process);
}
}The log output shows each node being executed in order, confirming the chain works as intended.
Strategy + Chain of Responsibility + Composite Implementation
The entry class is extended from SignConfig to obtain a sorted Map of interceptors based on the ContractSignEnum values, eliminating manual bean injection.
@Slf4j
@Component
public class ContractSignProcessor
extends SignConfig implements Processor
{
@Override
public ContractResponse process(T paramter) {
List
> 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();
}
}Custom Annotation
package com.example.springboottest.chainresponsibility.annotations;
import com.example.springboottest.chainresponsibility.Enum.ContractSignEnum;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContractSign {
ContractSignEnum.SignChannel SIGN_CHANNEL();
}Enum for Signature Channels
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; }
// getters omitted
}
}SignConfig – Scans Annotated Interceptors
public class SignConfig {
@Resource
protected List
contractSignList;
protected static final Map
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);
}
});
}
}Using the annotation on an interceptor, e.g.:
@Slf4j
@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
@Component
public class ContractSignCompactInitImpl
implements Interceptor
{ /* implementation as above */ }By inheriting SignConfig , the processor automatically builds an ordered chain based on the enum codes, simplifying future addition or removal of nodes.
Finally, a promotional note encourages readers to join a backend‑focused technical community for knowledge sharing.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.