Different implementations of dynamic proxies

Keywords: Java calculator JDK Spring

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


Posted by henryblake1979 on Sat, 14 Sep 2019 09:40:20 -0700