Mastering the Chain of Responsibility Pattern in Spring: A Hands‑On Guide
This article explains the Chain of Responsibility design pattern, showing how to refactor order processing in a Spring 5.2 Java 8 application by defining abstract handlers, implementing concrete handlers, configuring bean ordering, and extending the chain for new order types.
Introduction
Definition: The Chain of Responsibility pattern gives multiple objects a chance to handle a request, reducing coupling between sender and receiver. Handlers are linked in a chain; the request is passed along until one handler processes it.
Mnemonic: Responsibility passing
Examples: financial reimbursement, passing the drum, Sentinel (CtSph.java), Zookeeper, Nacos.
Create order → consume coupon → ship → rebate
Environment: JDK 1.8, Spring 5.2.x
Code Implementation
The implementation uses an abstract class
AbstractOrderHandlerto define the handler contract. In the example there are four concrete handlers:
CreateOrderHandler,
UseCouponOrderHandler,
GoodsDeliverOrderHandler, and
RebateOrderHandler. This design splits a large method into reusable, low‑coupling handler classes.
Define Abstract Methods
The abstract class defines two key methods:
<code>public abstract class AbstractOrderHandler {
/**
* Distinguish type
*/
protected abstract OrderTypeEnum getTypeEnum();
/**
* Core processing
*/
public void doHandle(OrderHandleContext context, OrderHandlerChain chain, Object... args) {
// Can I handle?
if (Objects.isNull(getTypeEnum()) || Objects.equals(context.getTypeEnum(), getTypeEnum())) {
// Let me handle
doHandle(context, args);
}
// Pass to next handler
chain.handle(context, args);
}
/**
* Concrete business logic
*/
protected abstract void doHandle(OrderHandleContext context, Object... args);
}
</code>Chain Implementation
Two concrete handlers are shown:
CreateOrderHandlerand
RebateOrderHandler. Each overrides
getTypeEnumand implements its own
doHandlelogic.
<code>// Create order
@Slf4j
@Service
@Order(100)
public class CreateOrderHandler extends AbstractOrderHandler {
@Override
protected OrderTypeEnum getTypeEnum() {
return null;
}
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
log.info("default create order ... ");
// lock inventory
lockSku(context, args);
// save order
saveOrder(context);
// deduct inventory
deductSku(context, args);
}
}
// Rebate order
@Service
@Slf4j
@Order(200)
public class RebateOrderHandler extends AbstractOrderHandler {
@Override
protected OrderTypeEnum getTypeEnum() {
return null;
}
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
log.info("default rebate order ... ");
}
}
</code>Define Entry Point
OrderHandlerChainis the external entry that obtains the sorted list of
AbstractOrderHandlerbeans (using Spring's @Order) and executes them.
<code>@Slf4j
@Component
public class OrderHandlerChain {
@Autowired
private List<AbstractOrderHandler> chain;
@Autowired
private ApplicationContext applicationContext;
public void handle(OrderHandleContext context, Object... objects) {
if (context.getPos() < chain.size()) {
AbstractOrderHandler handler = chain.get(context.getPos());
// move position in the chain
context.setPos(context.getPos() + 1);
handler.doHandle(context, this, objects);
}
}
}
</code>Business Extension
When new order types appear (e.g., car orders), a new handler can be added by returning a customized
AbstractOrderHandlerbean with a specific
OrderTypeEnum. This avoids creating a separate Java file while still generating a .class file, consuming JVM Metaspace.
<code>@Configuration
public class CarOrderHandlers {
/**
* Car order creation
*/
@Bean(name = "createOrderByCar")
public AbstractOrderHandler createOrderByCar() {
return new CreateOrderHandler() {
@Override
protected OrderTypeEnum getTypeEnum() {
return OrderTypeEnum.Car;
}
@Autowired
private ApplicationContext applicationContext;
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
System.out.println("car order create ....");
}
};
}
}
</code>Test Code
The test simply creates a
OrderHandleContext, sets the type to
Car, and invokes
chain.handle.
<code>@Slf4j
@SpringBootTest(classes = App.class)
public class OrderHandlerChainTest {
@Resource
private OrderHandlerChain chain;
@Test
public void testOrderChain() {
OrderHandleContext context = new OrderHandleContext();
context.setTypeEnum(OrderTypeEnum.Car);
chain.handle(context, null);
}
}
</code>Conclusion
The article demonstrates how Spring’s bean ordering and @Bean definitions can be leveraged to implement the Chain of Responsibility pattern. The refactored design combines aspects of the Strategy pattern (multiple vertical routing options) with the Chain of Responsibility (horizontal processing segmentation), yielding a flexible and low‑coupling solution.
References
https://www.cnblogs.com/vcmq/p/12542399.html
http://c.biancheng.net/view/1383.html
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
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.