Fundamentals 10 min read

Master Proxy and Decorator Patterns: From a Coffee Demo to Custom Spring AOP

This article walks through the proxy and decorator design patterns using a coffee‑ordering example, compares their differences, explains how Spring AOP relies on proxy mechanisms, and demonstrates building a custom dynamic proxy for SMS cost tracking with JDK InvocationHandler and cglib.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Master Proxy and Decorator Patterns: From a Coffee Demo to Custom Spring AOP

Introduction

Opening a to‑do note, the author promises to explain the proxy pattern and its relation to Spring AOP.

Proxy and Decorator

Scenario Description

Proxy means "substitute" and must implement the same contract as the original class.

The proxy pattern is similar to the decorator pattern; the author uses a coffee example to illustrate both.

Quiet afternoon, a coffee shop, a cup of coffee.

Basic Implementation

Define a coffee interface:

public interface Coffee {
    /** Print the material of the coffee */
    void printMaterial();
}

Default bitter coffee implementation:

public class BitterCoffee implements Coffee {
    @Override
    public void printMaterial() {
        System.out.println("咖啡");
    }
}

Simple ordering logic:

public class Main {
    public static void main(String[] args) {
        Coffee coffee = new BitterCoffee();
        coffee.printMaterial();
    }
}

Result: a cup of bitter coffee.

Decorator Pattern

Adding sugar via a decorator:

public class SugarDecorator implements Coffee {
    private final Coffee coffee;
    public SugarDecorator(Coffee coffee) { this.coffee = coffee; }
    @Override
    public void printMaterial() {
        System.out.println("糖");
        this.coffee.printMaterial();
    }
}

Usage:

public class Main {
    public static void main(String[] args) {
        Coffee coffee = new BitterCoffee();
        coffee = new SugarDecorator(coffee);
        coffee.printMaterial();
    }
}

The output shows both sugar and coffee material.

Difference

Decorator wraps an existing object and adds behavior (e.g., sugar), while proxy creates a new object that directly provides the desired behavior (e.g., a coffee with sugar already built in).

// Decorator
Coffee coffee = new BitterCoffee();
coffee = new SugarDecorator(coffee);

// Proxy
Coffee coffee = new CoffeeWithSugar();

Both achieve similar results but differ in intent and implementation.

AOP

Aspect‑Oriented Programming complements OOP. Spring AOP is essentially a proxy‑based implementation of AOP.

Scenario: SMS Cost Counting

Define an SMS service interface and a simple implementation that prints a verification code.

public interface SMSService {
    void sendMessage();
}

public class SMSServiceImpl implements SMSService {
    @Override
    public void sendMessage() {
        System.out.println("【梦云智】您正在执行重置密码操作,您的验证码为:1234,5分钟内有效,请不要将验证码转发给他人。");
    }
}

Normally, a Spring AOP aspect would intercept sendMessage to count cost. To implement it without Spring, a JDK dynamic proxy is created.

JDK Dynamic Proxy

InvocationHandler that records money spent:

public class MoneyCountInvocationHandler implements InvocationHandler {
    private final Object target;
    private Double moneyCount = 0.0;
    public MoneyCountInvocationHandler(Object target) { this.target = target; }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        moneyCount += 0.07;
        System.out.println("发送短信成功,共花了:" + moneyCount + "元");
        return result;
    }
}

Proxy creation in Main:

public class Main {
    public static void main(String[] args) {
        SMSService smsService = new SMSServiceImpl();
        smsService = (SMSService) Proxy.newProxyInstance(
            Main.class.getClassLoader(),
            new Class[]{SMSService.class},
            new MoneyCountInvocationHandler(smsService)
        );
        smsService.sendMessage();
        smsService.sendMessage();
    }
}

Limitations of JDK Proxy

JDK proxies only work for interfaces. When a class is injected directly (e.g., @Autowired private SMSServiceImpl smsService;), JDK proxy cannot create a proxy.

cglib Dynamic Proxy

cglib generates a subclass of the target class and weaves the aspect logic into the subclass, thus supporting class‑based injection. However, final methods cannot be overridden, so cglib cannot proxy them.

Conclusion

Learning frameworks is not just about productivity; it is about understanding the designers' intent. If we grasp these ideas, we can eventually design our own frameworks.
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.

Design PatternsJavaaopDynamic ProxyDecorator PatternProxy Patterncglib
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

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.