1. Overview
In this article, we will learn the ProxyFactory of Spring which is used to create an AOP proxy programmatically.
2. ProxyFactory
You can create an AOP proxy programmatically using the ProxyFactory class and invoke the target class methods using the proxy object. This can also allow you to run code before or after method execution using Interceptor.
As a prerequisite, see AOP proxy documentation to understand the JDK dynamic proxy and CGLIB proxy mechanisms.
2.1. Programmatic creation of CGLIB proxy
Let’s see a sample code to create a CGLIB proxy.
Create a ProxyFactory instance and pass the target class to be proxied to the setTarget
function. After that, you can call the getProxy
function on the ProxyFactory
instance to get the proxy object of the target class.
ProxyFactory
uses CGLIB proxy mechanism as there is no interface involved. It creates a proxy object that would be a subclass of the target class. If you specify any interface, then the JDK Dynamic proxy is used.
Suppose you want to intercept calls to the target class and run some code before or after the method execution. For this, create a class that extends MethodInterceptor
class and implement the invoke(MethodInvocation)
function to change the original behavior of the target methods. This is the same as Aspect around advice method.
So whenever you call any methods of the target class via proxy object, the MethodInterceptor
subclass intercepts the call. Inside the invoke
function, you can write whatever code you want to execute and call the proceed
function to execute the target method.
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(); TargetClass target = new TargetClass(); // direct call on target class instance without proxy target.print(); // create an instance of ProxyFactory ProxyFactory pf = new ProxyFactory(); // Add MethodInterceptor as advice pf.addAdvice(new AdviceInterceptor()); // set the target class you want to proxy pf.setTarget(target); // Get the proxy TargetClass proxy = (TargetClass) pf.getProxy(); // Call target class print function via proxy proxy.print(); } } class TargetClass { public void print() { System.out.print("Target Method execution"); } } class AdviceInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { // run code before method execution System.out.print("Before target method execution " + invocation.getMethod().getName()); // target method execution Object retVal = invocation.proceed(); System.out.println("After target method execution " + invocation.getMethod().getName()); return retVal; } }
Result:
Target Method execution
Before target method execution print
Target Method execution
After target method execution print
You can also prevent the target method execution by not calling the invocation.proceed
function if a certain condition doesn’t match. For example, the target method does not execute in the below code.
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; import com.tedbob.bls.AlternativeCurrenciesRepository; import com.tedbob.bls.CountryRepository; import com.tedbob.bls.CurrencyService; import com.tedbob.ds.CurrencyId; public class Runner { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); context.registerShutdownHook(); TargetClass target = new TargetClass(); target.print(); ProxyFactory pf = new ProxyFactory(); pf.addAdvice(new AdviceInterceptor()); pf.setTarget(target); TargetClass proxy = (TargetClass) pf.getProxy(); proxy.print(); } } class TargetClass { public void print() { System.out.println("Target Method execution"); } } class AdviceInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { // Here, we are not calling target method so it wont be executed System.out.println("Prevented target method execution " + invocation.getMethod().getName()); return null; } }
Result:
Target Method execution
Prevented target method execution print
2.2. Programmatic creation of JDKDynamic proxy
If you specify any interface, then ProxyFactory
uses JDKDynamic proxy. The proxy object will implement the interface class and proxies all the interface methods. You can use setInterfaces
function to add one or more interface classes.
Let’s take the same example. Here, any call to the target class goes through the Proxy object and its interceptor intercept any interface call to the target class. Note, proxy object will not be a subclass of Target class so you cannot type cast proxy object as target class.
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; import com.tedbob.bls.AlternativeCurrenciesRepository; import com.tedbob.bls.CountryRepository; import com.tedbob.bls.CurrencyService; import com.tedbob.ds.CurrencyId; public class Runner { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); context.registerShutdownHook(); TargetClass target = new TargetClass(); target.print(); ProxyFactory pf = new ProxyFactory(); pf.addAdvice(new AdviceInterceptor()); pf.setTarget(target); pf.setInterfaces(BaseInterface.class); BaseInterface proxy = (BaseInterface) pf.getProxy(); proxy.print(); } } interface BaseInterface { void print(); } class TargetClass implements BaseInterface { @Override public void print() { System.out.println("Target Method execution"); } } class AdviceInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Before Target method execution " + invocation.getMethod().getName()); return invocation.proceed(); } }
3. Conclusion
In this article, we saw the ProxyFactory example and few examples to understand the various scenarios.
One thought on “ProxyFactory Spring with example”