Backend Development 6 min read

Mastering the Chain of Responsibility Pattern in Java Spring: A Step‑by‑Step Guide

This article explains the Chain of Responsibility design pattern using a real‑world iron‑chain analogy, then demonstrates a complete Java Spring implementation for order validation, including interface definition, abstract base class, concrete handlers, Spring configuration, service, and controller code.

Lobster Programming
Lobster Programming
Lobster Programming
Mastering the Chain of Responsibility Pattern in Java Spring: A Step‑by‑Step Guide

In real life an iron chain consists of linked rings; each ring can be likened to an object in the Chain of Responsibility pattern, where each object handles a single piece of logic and passes the request along the chain until processing is complete.

Practical Application in a Project

We apply the pattern to validate order parameters: first checking if the order ID is null, then the buyer, and finally the shipping address. The workflow diagram (image) illustrates the validation steps.

The project structure is shown in the following diagram.

Implementation Details

1. Define the chain interface

<code>public interface OrderCheckHandler {
    void check(OrderCheckParam orderCheckParam);
    OrderCheckHandler setNext(OrderCheckHandler nextOrderCheckHandler);
}</code>

2. Extract common chain logic

<code>public abstract class BaseCheckOrderHandler implements OrderCheckHandler {
    private OrderCheckHandler nextOrderCheckHandler;

    @Override
    public void check(OrderCheckParam orderCheckParam) {
        this.currentCheck(orderCheckParam);
        if (Objects.nonNull(nextOrderCheckHandler)) {
            nextOrderCheckHandler.check(orderCheckParam);
        }
    }

    protected abstract void currentCheck(OrderCheckParam orderCheckParam);

    @Override
    public OrderCheckHandler setNext(OrderCheckHandler nextOrderCheckHandler) {
        this.nextOrderCheckHandler = nextOrderCheckHandler;
        return this.nextOrderCheckHandler;
    }
}</code>

3. Concrete validation handlers

<code>// Order ID validation
@Service
public class OrderIdCheckHandler extends BaseCheckOrderHandler {
    @Override
    protected void currentCheck(OrderCheckParam orderCheckParam) {
        if (orderCheckParam.getOrderId() == null) {
            throw new RuntimeException("订单id为空");
        }
    }
}

// Buyer validation
@Service
public class BuyerCheckHandler extends BaseCheckOrderHandler {
    @Override
    protected void currentCheck(OrderCheckParam orderCheckParam) {
        if (orderCheckParam.getBuyer() == null) {
            throw new RuntimeException("用户不可以为空");
        }
    }
}

// Address validation
@Service
public class AddressCheckHandler extends BaseCheckOrderHandler {
    @Override
    protected void currentCheck(OrderCheckParam orderCheckParam) {
        if (orderCheckParam.getAddress() == null) {
            throw new RuntimeException("用户地址不可以为空");
        }
    }
}</code>

4. Wire the chain with Spring

<code>@Configuration
public class OrderCheckConfig {
    @Bean
    public BuyerCheckHandler buyerCheckHandler() { return new BuyerCheckHandler(); }
    @Bean
    public OrderIdCheckHandler checkOrderIdHandler() { return new OrderIdCheckHandler(); }
    @Bean
    public AddressCheckHandler addressCheckHandler() { return new AddressCheckHandler(); }

    // Define execution order of the chain
    @Bean
    public OrderCheckHandler orderCheckHandler() {
        OrderIdCheckHandler orderIdCheckHandler = this.checkOrderIdHandler();
        orderIdCheckHandler.setNext(this.buyerCheckHandler()).setNext(this.addressCheckHandler());
        return orderIdCheckHandler;
    }
}</code>

5. Service invoking the chain

<code>@Service
public class OrderService {
    @Resource
    private OrderCheckHandler orderCheckHandler;

    public String checkOrderParam(OrderCheckParam param) {
        orderCheckHandler.check(param);
        return "success";
    }
}</code>

6. Controller exposing a test endpoint

<code>@RestController
@RequestMapping("/test")
public class ZeRenLianTestController {
    @Resource
    private OrderService orderService;

    @GetMapping("/checkOrder")
    public String checkOrder() {
        OrderCheckParam orderCheckParam = new OrderCheckParam();
        orderCheckParam.setAddress("123");
        orderCheckParam.setOrderId("1235689");
        return orderService.checkOrderParam(orderCheckParam);
    }
}</code>

The test result is shown in the following image.

If a new validation, such as logistics verification, is needed, simply add a new handler class and include it in the Spring configuration to extend the chain.

Key Takeaways

(1) Keep the chain structure clear and avoid overly long chains to maintain performance.

(2) Each handler should have a single responsibility, focusing only on its own logic.

(3) Typical scenarios for the Chain of Responsibility include multiple objects processing the same request, such as approval workflows, parameter validation, and logging chains.

backendchain of responsibilityJavaSpringdesign pattern
Lobster Programming
Written by

Lobster Programming

Sharing insights on technical analysis and exchange, making life better through technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.