Mastering the Chain of Responsibility Pattern in Spring Boot: 3 Practical Implementations
This article explains the Chain of Responsibility design pattern, outlines typical use cases, and demonstrates three concrete ways to implement it in Spring Boot with full Java code examples, unit tests, and execution results, helping developers build clean, extensible backend workflows.
What is the Chain of Responsibility Pattern?
The Chain of Responsibility pattern creates a processing chain between a request sender and multiple handlers, decoupling the sender from the receivers and allowing flexible, reusable, and extensible request handling.
Typical scenarios include Tomcat encoding handling, Spring Boot interceptors/filters, Netty pipelines, payment risk control, and log level processing.
Code Practice in Spring Boot
We illustrate the pattern using an order‑processing example that is split into independent validation steps.
Method 1 – Manual Sorting with sort()
Define a simple order context and a handler interface:
<code>public class OrderContext {
private String seqId;
private String userId;
private Long skuId;
private Integer amount;
private String userAddressId;
// getters & setters
}
public interface OrderHandleIntercept {
int sort();
OrderAddContext handle(OrderAddContext context);
}</code>Implement three handlers and assign execution order via
sort():
<code>@Component
public class RepeatOrderHandleInterceptService implements OrderHandleIntercept {
@Override
public int sort() { return 1; }
@Override
public OrderAddContext handle(OrderAddContext context) {
System.out.println("Check for duplicate order by seqId");
return context;
}
}
@Component
public class ValidOrderHandleInterceptService implements OrderHandleIntercept {
@Override
public int sort() { return 2; }
@Override
public OrderAddContext handle(OrderAddContext context) {
System.out.println("Validate request parameters and fetch bank account");
return context;
}
}
@Component
public class BankOrderHandleInterceptService implements OrderHandleIntercept {
@Override
public int sort() { return 3; }
@Override
public OrderAddContext handle(OrderAddContext context) {
System.out.println("Check bank account balance for order amount");
return context;
}
}</code>Create a chain manager that collects the beans, sorts them by
sort(), and executes them sequentially:
<code>@Component
public class OrderHandleChainService implements ApplicationContextAware {
private List<OrderHandleIntercept> handleList = new ArrayList<>();
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
Map<String, OrderHandleIntercept> map = ctx.getBeansOfType(OrderHandleIntercept.class);
handleList = map.values().stream()
.sorted(Comparator.comparing(OrderHandleIntercept::sort))
.collect(Collectors.toList());
}
public OrderAddContext execute(OrderAddContext context) {
for (OrderHandleIntercept h : handleList) {
context = h.handle(context);
}
return context;
}
}</code>Unit test:
<code>@RunWith(SpringRunner.class)
@SpringBootTest
public class ChainTest {
@Autowired
private OrderHandleChainService chainService;
@Test
public void test() {
chainService.execute(new OrderAddContext());
}
}</code>Result:
<code>Check for duplicate order by seqId
Validate request parameters and fetch bank account
Check bank account balance for order amount
</code>Method 2 – Using Spring’s @Order Annotation
Annotate each handler with
@Orderto define execution priority, letting Spring inject an ordered
List<OrderHandleIntercept>automatically:
<code>@Order(1)
@Component
public class RepeatOrderHandleInterceptService implements OrderHandleIntercept { /* ... */ }
@Order(2)
@Component
public class ValidOrderHandleInterceptService implements OrderHandleIntercept { /* ... */ }
@Order(3)
@Component
public class BankOrderHandleInterceptService implements OrderHandleIntercept { /* ... */ }
@Component
public class OrderHandleChainService {
@Autowired
private List<OrderHandleIntercept> handleList;
public OrderAddContext execute(OrderAddContext ctx) {
for (OrderHandleIntercept h : handleList) {
ctx = h.handle(ctx);
}
return ctx;
}
}</code>The test output is identical to Method 1.
Method 3 – Abstract Class Chain
Define an abstract handler that holds a reference to the next node:
<code>public abstract class AbstractOrderHandle {
private AbstractOrderHandle next;
public OrderAddContext execute(OrderAddContext ctx) {
ctx = handle(ctx);
if (next != null) {
next.execute(ctx);
}
return ctx;
}
public abstract OrderAddContext handle(OrderAddContext ctx);
public void setNext(AbstractOrderHandle next) { this.next = next; }
}</code>Implement concrete handlers extending the abstract class and annotate them with
@Orderfor optional sorting:
<code>@Order(1)
@Component
public class RepeatOrderHandle extends AbstractOrderHandle {
@Override
public OrderAddContext handle(OrderAddContext ctx) {
System.out.println("Check for duplicate order by seqId");
return ctx;
}
}
@Order(2)
@Component
public class ValidOrderHandle extends AbstractOrderHandle { /* similar */ }
@Order(3)
@Component
public class BankOrderHandle extends AbstractOrderHandle { /* similar */ }
</code>Manager that builds the chain and starts execution:
<code>@Component
public class OrderHandleManager {
@Autowired
private List<AbstractOrderHandle> orderHandleList;
@PostConstruct
public void init() {
Collections.sort(orderHandleList, AnnotationAwareOrderComparator.INSTANCE);
for (int i = 0; i < orderHandleList.size() - 1; i++) {
orderHandleList.get(i).setNext(orderHandleList.get(i + 1));
}
orderHandleList.get(orderHandleList.size() - 1).setNext(null);
}
public OrderAddContext execute(OrderAddContext ctx) {
return orderHandleList.get(0).execute(ctx);
}
}</code>Unit test produces the same console output as the previous methods.
Conclusion
Applying the Chain of Responsibility pattern in Spring Boot can dramatically reduce business‑logic complexity, improve readability, and make the codebase easier to extend. Choose the implementation style that best fits your project’s architecture—manual sorting,
@Orderannotation, or an abstract‑class chain.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.