Spring Boot AOP: Part 3 - CGLib vs JDK Proxies
Two Flavors of Magic
In Part 2, we learned that Spring AOP uses proxies to intercept method calls. But hereโs something interesting: Spring can create proxies in two different ways.
Think of it like having two different tools to accomplish the same job:
- A Swiss Army knife (CGLib) - works in almost any situation
- A specialized tool (JDK Proxy) - faster but only works in specific cases
Letโs explore both and understand when to use each.
Type 1: JDK Dynamic Proxies (Interface-Based)
JDK proxies are part of standard Java (since Java 1.3). They work by implementing interfaces.
How It Works
JDK proxies require your class to implement an interface. Spring creates a proxy that implements the same interface.
// Step 1: Define an interface
public interface PaymentService {
void processPayment(String userId, BigDecimal amount);
void refundPayment(String transactionId);
}
// Step 2: Implement the interface
@Service
public class PaymentServiceImpl implements PaymentService {
@Override
public void processPayment(String userId, BigDecimal amount) {
System.out.println("Processing payment for user: " + userId);
// business logic
}
@Override
public void refundPayment(String transactionId) {
System.out.println("Refunding transaction: " + transactionId);
// business logic
}
}
When Spring creates a proxy, it does something like this:
PaymentService proxy = (PaymentService) Proxy.newProxyInstance(
classLoader,
new Class[] { PaymentService.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// Execute @Before advice
loggingAspect.logBefore();
// Call the actual method
Object result = method.invoke(actualService, args);
// Execute @After advice
loggingAspect.logAfter();
return result;
}
}
);
The Key Point
The proxy implements the interface, not extends the class. This is important!
PaymentService (interface)
โ implements
|
PaymentServiceImpl (your class)
PaymentService (interface)
โ implements
|
$Proxy0 (JDK proxy)
โ delegates to โ PaymentServiceImpl
Pros of JDK Proxies
๐ Part of standard Java - No external dependencies needed
๐ Lightweight - Minimal overhead
๐ Fast - Slightly better performance than CGLib
๐ Clean separation - Encourages interface-based design
Cons of JDK Proxies
๐ Requires an interface - Your class must implement one
๐ Canโt proxy classes directly - Only works with interfaces
๐ Less flexible - More restrictive than CGLib
Type 2: CGLib Proxies (Class-Based)
CGLib (Code Generation Library) is more powerful. It can create proxies for any class, even without an interface.
How It Works
CGLib creates a subclass of your class at runtime using bytecode generation.
// No interface needed!
@Service
public class PaymentService {
public void processPayment(String userId, BigDecimal amount) {
System.out.println("Processing payment for user: " + userId);
// business logic
}
public void refundPayment(String transactionId) {
System.out.println("Refunding transaction: " + transactionId);
// business logic
}
}
CGLib generates a subclass like this:
public class PaymentService$$EnhancerBySpringCGLIB$$a1b2c3d4 extends PaymentService {
private PaymentService target;
private LoggingAspect loggingAspect;
@Override
public void processPayment(String userId, BigDecimal amount) {
// Execute @Before advice
loggingAspect.logBefore();
// Call the parent method
super.processPayment(userId, amount);
// Execute @After advice
loggingAspect.logAfter();
}
@Override
public void refundPayment(String transactionId) {
// Same pattern...
loggingAspect.logBefore();
super.refundPayment(transactionId);
loggingAspect.logAfter();
}
}
The Key Point
The proxy extends your class. Itโs a subclass!
PaymentService (your class)
โ extends
|
PaymentService$$EnhancerBySpringCGLIB (proxy)
Pros of CGLib Proxies
๐ Works with any class - No interface required
๐ More flexible - Can proxy classes directly
๐ Default in Spring Boot - No configuration needed
๐ Widely used - Battle-tested in production
Cons of CGLib Proxies
๐ Slightly slower - More overhead than JDK proxies
๐ Canโt proxy final classes - Subclassing wonโt work
๐ Canโt proxy final methods - Canโt override them
๐ Requires CGLib library - Extra dependency (but included in Spring Boot)
Which One Does Spring Use?
By default, Spring Boot uses CGLib proxies for everything.
You can verify this yourself:
@Service
public class PaymentService {
public void processPayment(String userId, BigDecimal amount) {
// Print the actual class name
System.out.println("Class: " + this.getClass().getName());
System.out.println("Simple name: " + this.getClass().getSimpleName());
}
}
When you run this with AOP enabled, youโll see:
Class: com.app.service.PaymentService$$EnhancerBySpringCGLIB$$a1b2c3d4
Simple name: PaymentService$$EnhancerBySpringCGLIB$$a1b2c3d4
See that $$EnhancerBySpringCGLIB? Thatโs your proofโitโs a CGLib proxy!
Fun fact: The random characters at the end (a1b2c3d4) are generated by CGLib to ensure unique class names. Each proxy gets a different suffix.
Switching to JDK Proxies
If you prefer JDK proxies (and your classes implement interfaces), you can configure Spring to use them:
# application.properties
spring.aop.proxy-target-class=false
Or in YAML:
# application.yml
spring:
aop:
proxy-target-class: false
Important: If you set proxy-target-class=false but your class doesnโt implement an interface, Spring will throw an error at startup:
Cannot create JDK dynamic proxy: target class does not implement any interfacesMake sure all your AOP-enabled classes implement interfaces if you use this setting!
Side-by-Side Comparison
| Feature | CGLib Proxy | JDK Proxy |
|---|---|---|
| Requires interface? | ๐ซ No | โ๏ธ Yes |
| How it works | Subclassing (extends) | Interface implementation |
| Can proxy final classes? | ๐ซ No | N/A |
| Can proxy final methods? | ๐ซ No | โ๏ธ Yes (on interface) |
| Performance | Slightly slower | Slightly faster |
| Memory usage | Slightly higher | Slightly lower |
| Spring Boot default | โ๏ธ Yes | ๐ซ No |
| Configuration | Default | proxy-target-class=false |
| External dependency | CGLib (included) | None (JDK built-in) |
| Use case | General purpose | Interface-based design |
Practical Example: Seeing Proxies in Action
Letโs create a simple example to see proxies at work:
@Service
public class OrderService {
public void createOrder(String userId, List<Item> items) {
System.out.println("=== Inside createOrder ===");
System.out.println("Class: " + this.getClass().getSimpleName());
Order order = new Order(userId, items);
order.calculateTotal();
System.out.println("Order created: " + order.getId());
}
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.app.service.OrderService.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("โ [BEFORE] Calling: " + joinPoint.getSignature().getName());
}
@AfterReturning("execution(* com.app.service.OrderService.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("โ [AFTER] Finished: " + joinPoint.getSignature().getName());
}
}
When you call orderService.createOrder(), youโll see:
โ [BEFORE] Calling: createOrder
=== Inside createOrder ===
Class: OrderService$$EnhancerBySpringCGLIB$$a1b2c3d4
Order created: ORD-12345
โ [AFTER] Finished: createOrder
Notice:
- The
@Beforeadvice runs first - The actual method executes
- The class name shows itโs a CGLib proxy
- The
@Afteradvice runs last
When to Use Which Proxy?
Recommendation: Stick with the default (CGLib) unless you have a specific reason to change. It works in 99% of cases and requires less boilerplate.
Key Takeaways
- Spring supports two proxy types: CGLib (class-based) and JDK (interface-based)
- CGLib is the default and works with any class
- JDK proxies require interfaces but are slightly faster
- Only public methods can be effectively proxied
- Final classes/methods canโt be proxied by CGLib
- Understanding proxy limitations helps you write better AOP code
๐ฌ Want to See How Proxies Work Internally?
Curious about how JDK Dynamic Proxies and CGLib proxies actually work under the hood? Iโve created a hands-on demo project that shows you the internals!
๐ฆ Demo Project: Spring Dynamic Proxy Demo
This repository contains working examples that demonstrate:
- How JDK Dynamic Proxies create proxy instances
- How CGLib generates subclasses at runtime
- Side-by-side comparison of both approaches
- Real code you can run and experiment with
Perfect for understanding the low-level mechanics behind Spring AOP!
Whatโs Next?
Letโs write some code !
๐ Series: Spring Boot - Aspect Oriented Programming
Part 3 of 5
Comments
Join the discussion and share your thoughts