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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
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.
