Fundamentals 10 min read

Mastering the Chain of Responsibility Pattern in Java: A Practical Guide

This article explains the Chain of Responsibility design pattern, outlines its four roles, demonstrates a real‑world Java implementation for encrypted request handling, and shows how to build an extensible processing chain that improves code readability and maintainability.

macrozheng
macrozheng
macrozheng
Mastering the Chain of Responsibility Pattern in Java: A Practical Guide

1. Introduction

The Chain of Responsibility pattern creates a processing chain between a requester and a receiver, decoupling the sender from the handler . It involves four roles: request, abstract handler, concrete handler, and next handler.

2. Example

In many projects, request parameters are encrypted before being sent over the network. The following example shows how to use the Chain of Responsibility pattern to decrypt, validate, and encapsulate request data.

2.1 AES Utility

public class AESUtil {
    private static Logger log = LoggerFactory.getLogger(AESUtil.class);
    private static final String AES = "AES";
    private static final String AES_CVC_PKC = "AES/CBC/PKCS7Padding";
    static { Security.addProvider(new BouncyCastleProvider()); }
    /**
     * Encrypt
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(AES_CVC_PKC);
            IvParameterSpec iv = new IvParameterSpec(new byte[16]);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
            byte[] encrypted = cipher.doFinal(content.getBytes());
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            log.warn("AES encryption failed, param:{}, error:{}", content, ExceptionUtils.getStackTrace(e));
            return "";
        }
    }
    /**
     * Decrypt
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String decrypt(String content, String key) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), AES);
            Cipher cipher = Cipher.getInstance(AES_CVC_PKC);
            IvParameterSpec iv = new IvParameterSpec(new byte[16]);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
            byte[] encrypted = Base64.getDecoder().decode(content);
            byte[] original = cipher.doFinal(encrypted);
            return new String(original, "UTF-8");
        } catch (Exception e) {
            log.warn("AES decryption failed, param:{}, error:{}", content, ExceptionUtils.getStackTrace(e));
            return "";
        }
    }
    public static void main(String[] args) throws Exception {
        String key = "1234567890123456";
        String content = "{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}";
        String encryptContext = encrypt(content, key);
        System.out.println("Encrypted: " + encryptContext);
        String decryptContext = decrypt(encryptContext, key);
        System.out.println("Decrypted: " + decryptContext);
    }
}

Running the above prints the encrypted string and its decrypted JSON representation.

2.2 Context Entity

/**
 * Context
 */
public class ServiceContext {
    private String requestParam;
    private String jsonData;
    private String userCode;
    private String userPwd;
    // getters and setters omitted
    public ServiceContext() {}
    public ServiceContext(String requestParam) { this.requestParam = requestParam; }
}

2.3 Handler Interface

public interface HandleIntercept {
    /**
     * Process the context
     */
    ServiceContext handle(ServiceContext context);
}

2.4 Concrete Handlers

/** Decrypt request data */
public class DecodeDataHandle implements HandleIntercept {
    private String key = "1234567890123456";
    @Override
    public ServiceContext handle(ServiceContext context) {
        String jsonData = AESUtil.decrypt(context.getRequestParam(), key);
        if (StringUtils.isEmpty(jsonData)) {
            throw new IllegalArgumentException("Decryption failed");
        }
        context.setJsonData(jsonData);
        return context;
    }
}

/** Validate business data and encapsulate */
public class ValidDataHandle implements HandleIntercept {
    @Override
    public ServiceContext handle(ServiceContext context) {
        String jsonData = context.getJsonData();
        JSONObject jsonObject = JSONObject.parseObject(jsonData);
        if (!jsonObject.containsKey("userCode")) {
            throw new IllegalArgumentException("userCode cannot be empty");
        }
        context.setUserCode(jsonObject.getString("userCode"));
        if (!jsonObject.containsKey("userPwd")) {
            throw new IllegalArgumentException("userPwd cannot be empty");
        }
        context.setUserPwd(jsonObject.getString("userPwd"));
        return context;
    }
}

2.5 Chain Manager

/** Request processing chain manager */
public class HandleChain {
    private List<HandleIntercept> handleInterceptList = new ArrayList<>();
    public void addHandle(HandleIntercept handleIntercept) {
        handleInterceptList.add(handleIntercept);
    }
    public ServiceContext execute(ServiceContext context) {
        for (HandleIntercept handle : handleInterceptList) {
            context = handle.handle(context);
        }
        return context;
    }
}

2.6 Test Client

public class ChainClientTest {
    public static void main(String[] args) {
        String requestParam = "5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5";
        ServiceContext serviceContext = new ServiceContext(requestParam);
        HandleChain handleChain = new HandleChain();
        handleChain.addHandle(new DecodeDataHandle()); // decryption
        handleChain.addHandle(new ValidDataHandle()); // validation
        serviceContext = handleChain.execute(serviceContext);
        System.out.println("Result: " + JSONObject.toJSONString(serviceContext));
    }
}

Execution output shows the encrypted request, the decrypted JSON, and the populated fields in ServiceContext:

Result:{"jsonData":"{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}","requestParam":"5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5","userCode":"zhangsan","userPwd":"123456"}

The chain cleanly transforms the encrypted request into a fully populated context object, demonstrating superior readability and extensibility compared to nested if statements.

3. Application

The pattern is widely used in servlet filters. Each filter is a handler in the chain, registered in web.xml or via annotations, allowing modular processing of HTTP requests.

public class TestFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
    public void destroy() {}
    public void init(FilterConfig filterConfig) throws ServletException {}
}

4. Conclusion

Use the Chain of Responsibility pattern when multiple conditional checks share a common abstraction, such as encryption, validation, or business rule processing. It yields elegant, reusable, and easily extensible code, avoiding deep nesting and improving maintainability.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Chain of ResponsibilityJavaBackend Developmentdesign patternencryption
macrozheng
Written by

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.

0 followers
Reader feedback

How this landed with the community

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.