Dynamic Proxy Implementation
1.JDK Dynamic Proxy
jdk dynamic proxy implementation steps:
Premise: jdk dynamic proxy has restrictions, the target object of proxy must implement interface
Implementation: Using reflection API implementation, the specific implementation principle is not explained in detail here, only the implementation of dynamic proxy is explained here.
The following is a list of codes with three classes involved
-
Calculator.java [Interface for Target Object Implementation]
-
CalculatorImpl.java [Target Object]
-
Main.java [Program Entry Class]
//Interface for Target Object Implementation public interface Calculator { int add(int a, int b); }
//Target object public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { return a + b; } }
//Program Entry import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { //Create Target Object Calculator calculator = new CalculatorImpl(); //Create Proxy Object Calculator o = (Calculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), new Class[]{Calculator.class}, (proxy, method, args1) -> { //Define Other Procedures System.out.println("the method " + method.getName() + " is running ..."); //Execute Target Method return method.invoke(calculator, args1); }); o.add(2, 3); } }
Of the three classes, the interface and the target object have nothing to say but are simple.Core code is one line
Calculator o = (Calculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), new Class[]{Calculator.class}, (proxy, method, args1) -> { System.out.println("the method " + method.getName() + " is running ..."); return method.invoke(calculator, args1); });
The corresponding API is
//Returns an instance of the proxy class for the specified interface that assigns method calls to the specified call handler. public static Object newProxyInstance(ClassLoader loader,//classloader Class<?>[] interfaces,//Interface for Target Object Implementation InvocationHandler h)//Call Handler throws IllegalArgumentException
2.CGLIB dynamic proxy
The cglib dynamic proxy does not require the target class to inherit other classes or implement interfaces. It loads the class file of the target object class using the asm open source package and generates subclasses by modifying its byte code.In fact, cglib's API is very similar to jdk's dynamic proxy api.
//Target object, no interface is implemented here, so use CGLIB Proxy implementation public class Calculator{ public int add(int a, int b) { return a + b; } }
//Program Entry import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Calculator.class); //public void setCallback(final Callback callback) //This method requires Callback Callback, this is an interface, two common implementations //1.MethodInterceptor Interface, internal is intercept Method with the following parameters: //o For returned proxy objects, less common, easy to throw stack overflow //method For the reflection type of the target method, you can get the method name of the currently executing target method //objects by object Array, the actual parameter list for the target method //methodProxy Used to call an initial method, or to call a method of the same name of another class [different from the second parameter] method,Do not create target objects) //2.InvocationHandler Interface, internal is invoke Method with the following parameters: //invoke(java.lang.Object o, java.lang.reflect.Method method, java.lang.Object[] objects) //o For returned proxy objects, less common, easy to throw stack overflow //method Reflection type for target method //objects For parameter list enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> { String name = method.getName(); //Target Method System.out.println("Method" + name + "Running"); //Parameter list for target method System.out.println("Parameter list:"+objects); //Object result = method.invoke(o, objects); //Method throws a stack exception and needs to create a target object as a parameter Object result = methodProxy.invokeSuper(o, objects); System.out.println("Execution results:"+result); return result; }); //Create Proxy Object Calculator proxyObject = (Calculator) enhancer.create(); // System.out.println(proxyObject); proxyObject.add(1, 2); } }
3. Summarize jdk dynamic proxy and cglib proxy
jdk dynamic proxy requires target object to implement interface
The cglib proxy does not require the target object to implement interfaces or inherit objects
The jdk dynamic proxy uses the reflected API
The cglib proxy uses asm to parse and modify byte code files, and the resulting proxy object is essentially a subclass of the target object
Both methods are run-time dynamic binding.
4.aspectj proxy implementation
Spring AOP is essentially a combination of JDK Proxy dynamic proxy and CGLIB tools to implement a method of entry.Spring takes precedence over the JDK dynamic proxy and chooses to use CGLIB when the calling method is not an interface method.This section uses the annotated configuration of aspectj to implement dynamic proxies.
For a comparison of Spring AOP and Aspectj, see: https://juejin.im/post/5a695b3cf265da3e47449471
//Target object @Component public class Calculator2 { public int add(int a, int b) { return a + b; } }
//section @Component @Aspect public class LoggingAspect2 { /** * Define entry point expressions for easy management and code reuse */ @Pointcut(value = "execution(* aspectj.*.*.*(..))") public void declarePointcutExpression() { //There can be no code inside this method, just for markup Pointcut annotation } /** * Pre-notification, called before method is executed * * @param joinPoint Connection Point */ @Before(value = "declarePointcutExpression()") public void beforeExecute(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("Pre-notification:" + name + "Method [Previous] is executed..."); } /** * Post-notification, called after method execution, whether or not the method throws an exception * * @param joinPoint Connection Point */ @After(value = "declarePointcutExpression()") public void afterExecute(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("Post Notification: In" + name + "Method [post] is executed..."); } /** * Return notification, call after method is executed normally, get return value * * @param joinPoint Connection Point */ @AfterReturning(value = "declarePointcutExpression()", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { String name = joinPoint.getSignature().getName(); System.out.println("Normal return notification:" + name + "Method [Notify when normal execution returns] is executed..."); System.out.println("----------------------Method return value:---" + result + "----------"); } /** * Throws an exception notification, executes after a method throws an exception, restricts the method to execute after a specified exception is thrown, and obtains exception information * * @param joinPoint Connection Point */ @AfterThrowing(value = "declarePointcutExpression()", throwing = "e") public void afterThrowing(JoinPoint joinPoint, Throwable e) { String name = joinPoint.getSignature().getName(); System.out.println("Issue exception notification at" + name + "Method [throw exception] is executed"); System.out.println("----------------------Get exception information:---" + e.toString() + "-----------"); } /** * Wrap Notification: Code block execution can be defined before and after the method, which is the most powerful */ @Around(value = "declarePointcutExpression()") public Object around(ProceedingJoinPoint proceedingJoinPoint) { String name = proceedingJoinPoint.getSignature().getName(); System.out.println("Surround notification: in" + name + "Code blocks can be executed before and after methods"); System.out.println("Around Advice:This is the output log before the method call...."); Object result = null; try { //Call the corresponding method of the target object result = proceedingJoinPoint.proceed(); System.out.println("Around Advice:This is the output log after the method call...."); System.out.println("-----------------------------------Get Return Value Around Notification-------" + result + "------------"); } catch (Throwable throwable) { System.out.println("Around Advice:This is the output log after the method throws exception information...."); System.out.println("------------------------------------Get exception information around notifications-----------" + throwable + "---------------------------"); throwable.printStackTrace(); } System.out.println("Around Advice:This is the output log when the method returns normally..."); return result; } }
//Test Class @SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) public class CalculatorImpl2Test { @Autowired private Calculator2 calculator; @Test public void add() { calculator.add(1, 1); } }
Project Source: https://github.com/lingEric/dynamicproxy