1. Overview
In this article, we will explain the Spring AOP proxy mechanisms.
2. Spring AOP proxy
I would recommend you to go through this AOP Introduction article if you are not already aware of AOP.
Have you ever thought of how AOP works? You basically want to execute a piece of code before, after, or before and after method execution. It could be for performance logging, specific business rules, or exception handling.
How would Spring intercept the call to the target method and execute the code inside advice? It uses the proxying mechanisms. Spring AOP is proxy-based. It creates a proxy object for the target object.
If you call the target method, then the call actually goes to the proxy object. The proxy object then delegates to all the advice methods that apply to that method call. Then finally, the call goes to the target method.
Let’s take a simple example without proxy. In this below example, the un-proxied SimplePojo
class implements an interface Pojo
. The Runner
class creates an instance of the SimplePojo
and calls the foo
function directly on the instance.
Since we call the target method directly on the object reference, there is no way to execute the additional aspect code.
public class SimplePojo implements Pojo { public void foo() { this.bar(); } @Override public void bar() { System.out.println("Bar"); } } interface Pojo { void bar(); } public class Runner { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); context.registerShutdownHook(); SimplePojo simplePojo = new SimplePojo(); simplePojo.foo(); } }
Now, let’s see the same example with proxy to understand how it works. We are programmatically creating a proxy using ProxyFactory here. You can refer ProxyFactory
article for a thorough explanation.
The caller calls the bar
method on the proxy object (proxy.bar()
). The proxy object intercepts the call and executes the advice methods that apply to that method.
Finally, the proxy object calls the bar
method on the SimplePojo
object. So any call to the SimplePojo
object will go through the proxy object only.
package com.tedbob; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Runner { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); context.registerShutdownHook(); SimplePojo simplePojo = new SimplePojo(); simplePojo.bar(); ProxyFactory pf = new ProxyFactory(); pf.addAdvice(new AdviceInterceptor()); pf.setTarget(target); pf.setInterfaces(Pojo.class); Pojo proxy = (Pojo) pf.getProxy(); proxy.bar(); } }
Spring AOP uses either one of these proxying mechanisms
- JDK Dynamic proxy
- CGLIB proxy
2.1. JDK Dynamic proxy.
JDK Dynamic proxy is preferred whenever you have a choice.
If the target object to be proxied implements at least one interface, then the Spring AOP uses JDK dynamic proxy. It proxies all the interfaces implemented by the target class. If the target object does not implement any interfaces, then Spring AOP uses the CGLIB proxy.
The below target class CountriesRepositoryImpl implements an interface. So Spring uses JDK dynamic proxy. It creates a proxy object which also implements the same interface.
@Component public class CountriesRepositoryImpl implements CountryRepository { @Override public List<String> getCountries() { this.validate(); return countries; } private String validate() { } }
The client actually calls the proxy object. The proxy object intercepts the call to the target method and delegates the call to the advice and the target method.
But this does not support self invocation. Once the call has finally reached the target object, in this case, the CountriesRepositoryImpl
reference, any method calls that it may make on itself, such as this.validate()
are going to be invoked against this
reference, and not the proxy. This has important implications. It means that self-invocation will not result in the advice associated with a method invocation to execute.
package com.tedbob.aspects; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class CurrenciesRepositoryAspect { @Before("target(com.tedbob.bls.CountryRepository)") public void beforeTargetCountriesRepository() { System.out.println("beforeCountriesRepository"); } @After("this(com.tedbob.bls.CountryRepository)") public void afterCountriesRepository() { System.out.println("afterCountriesRepository"); } }
Limitations:
- Only interface methods are proxied. So non-interface methods are not proxied.
- Supports multiple interfaces
- Class must implement an interface.
- Does not support self-invocation.
2.2 CGLIB proxy
CGLib proxy is used by default if the target doesn’t implement any interface. To prevent the JDK dynamic proxy, you can use @EnableAspectJAutoProxy(proxyTargetClass=true) annotation in your class.
@Component @EnableAspectJAutoProxy(proxyTargetClass=true) public class CountriesRepositoryImpl implements CountryRepository { public int getCountriesCount() { return 0; } }
The Spring uses a CGLIB proxy to create a proxy object that will be a subclass of your target class as shown in the below diagram.
You can use CGLib proxy if you want to call advice for non-interface methods as well.
There are some limitations in using CGLIB proxy
- The final methods cannot be proxied, as CGLIB proxy object cannot override them.
- The target class cannot be final as cannot create a sub-class from the final class. The proxy object would be a subclass of the target class.
- You will need the CGLIB 2 binaries on your classpath, whereas dynamic proxies are available with the JDK. Spring will automatically warn you if it does not find the CGLIB library classes on the classpath.
- The constructor of your target object will be called twice. This is a natural effect of the CGLIB proxy model whereby a subclass is generated for each target object. For each target instance, CGLIB creates two objects: the actual target object and an instance of the subclass (proxy object) that implements the advice. This won’t happen with JDK proxies. Usually, calling the constructor of the target object twice is not an issue, as there are usually only assignments taking place and you won’t implement any actual logic in the constructor.
- Does not support self-invocation.
- Private methods are not proxied
The below example class will use CGLIB proxy as it doesn’t implement any interface.
@Component public class CountriesRepositoryImpl { public List<String> getCountries() { return countries; } }
3. Conclusion
In this article, we have explained the Spring AOP proxy mechanisms along with examples.