Welcome everyone to pay attention to my public number [old week chat architecture], Java back-end mainstream technology stack principle, source code analysis, architecture and a variety of Internet high concurrency, high performance, high availability of solutions.

AOP’s interception capabilities are implemented by dynamic proxies in Java. In plain English, we add section logic to the target class, generating enhanced target classes (the section logic is executed either before or after the target class function executes, or when the target class function throws an exception). Different entry times correspond to different types of interceptors, such as BeforeAdviseInterceptor, AfterAdviseInterceptor, ThrowsAdviseInterceptor, etc.).

How does a dynamic proxy weave an aspect logic (ADVISE) into a target class method? Let’s look at and implement two kinds of dynamic proxies that are used in AOP.

AOP source code uses two kinds of dynamic proxies to implement interception: JDK dynamic proxies and CGLIb dynamic proxies. Both methods exist at the same time, each has its advantages and disadvantages.

JDK dynamic proxy is implemented by Java’s internal reflection mechanism, while CGLIb dynamic proxy is implemented by ASM.

In general, reflection is more efficient during class generation, and ASM is more efficient during the related execution after class generation (you can solve this problem by caching classes generated by ASM). It is also important to note that JDK dynamic proxies can only be used if the target classes are based on a uniform interface. Without the foregoing, JDK dynamic proxies cannot be applied. From this, we can see that JDK dynamic proxy has certain limitations, and cGLIb, a third-party class library, is more widely used and has more advantages in efficiency.

1. Define interfaces and implementations

package com.proxy;

public interface UserService {
    public String getName(int id);

    public Integer getAge(int id);
}
Copy the code
package com.proxy;

public class UserServiceImpl implements UserService {
    @Override
    public String getName(int id) {
        System.out.println("------getName------");
        return "riemann";
    }

    @Override
    public Integer getAge(int id) {
        System.out.println("------getAge------");
        return 26; }}Copy the code

2, JDK dynamic proxy implementation

The JDK dynamic proxy is a form of proxy that is native to the JDK. The proxy class and the target class implement the same interface. The proxy class holds the target object to intercept methods. The second is that if you want to proxy-intercept the methods of the Target class, you need to make sure that these methods are declared in the interface, with a slightly limited implementation.

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    public Object target;

    MyInvocationHandler() {
        super(a); } MyInvocationHandler(Object target) {super(a);this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getName".equals(method.getName())) {
            System.out.println("++++++before " + method.getName() + "+ + + + + +");
            Object result = method.invoke(target, args);
            System.out.println("++++++after " + method.getName() + "+ + + + + +");
            return result;
        } else {
            Object result = method.invoke(target, args);
            returnresult; }}}Copy the code
package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main1 {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        InvocationHandler invocationHandler = new MyInvocationHandler(userService);
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),invocationHandler);
        System.out.println(userServiceProxy.getName(1));
        System.out.println(userServiceProxy.getAge(1)); }}Copy the code

Output results:

++++++before getName++++++
------getName------
++++++after getName++++++
riemann
------getAge------
26
Copy the code

3, cGLIb dynamic proxy implementation

Cglib is an excellent dynamic proxy framework. Its bottom layer uses ASM to dynamically generate subclasses of the proxy class in memory. Using Cglib, dynamic proxy function can be realized even if the proxy class does not implement any interface. CGLIB is easy to use and runs much faster than the JDK Proxy dynamic Proxy:

Cglib has two optional methods, inheritance and reference. The first is a dynamic proxy based on inheritance, so you can call the target method directly from super, but this is not supported in Spring, because the target object is not managed by Spring, so cglib uses the JDK’s approach. Intercepting methods by holding the target object.

CGLIB’s core classes: .net. Sf. Additional. Proxy. Enhanced Enhancer – the main kind of net. Sf. Additional. Proxy. The MethodInterceptor interceptor class – the main method, it is a Callback interface as part of the interface, Requires the user to realize net. Sf. Additional. Proxy. MethodProxy – the Java JDK. Lang. Reflect. Method the proxy class of a class, can easily realize the source object Method invocation, such as using: Object o = methodProxy.invokeSuper(proxy, args); // If the first argument is a proxied object, there is no problem with infinite loops.

Net. Sf. Additional. Proxy. MethodInterceptor interface is the most common type of callback, the callback, it is often proxy-based AOP is used to implement interception (intercept) method is called. Public Object Intercept (Object Object, java.lang.reflect.Method Method, Object[] args, MethodProxy proxy) throws Throwable;

The first argument is the proxy object, and the second and third arguments are the intercepting method and its parameters, respectively. The original Method may be through the use of Java. Lang. Reflect the general reflection of object Method calls, or use the net. Sf. Additional. Proxy. MethodProxy object invocation. Net. Sf. Additional. Proxy. MethodProxy is usually preferred to use, because it’s faster.

package com.proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
 
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("++++++before " + methodProxy.getSuperName() + "+ + + + + +");
        System.out.println(method.getName());
        Object o1 = methodProxy.invokeSuper(o, args);
        System.out.println("++++++before " + methodProxy.getSuperName() + "+ + + + + +");
        returno1; }}Copy the code
package com.proxy.cglib;
 
import com.meituan.hyt.test3.service.UserService;
import com.meituan.hyt.test3.service.impl.UserServiceImpl;
import net.sf.cglib.proxy.Enhancer;
 
public class Main2 {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
 
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(cglibProxy);
 
        UserService o = (UserService)enhancer.create();
        o.getName(1);
        o.getAge(1); }}Copy the code

Output results:

++++++before CGLIB$getName$0++++++
getName
------getName------
++++++before CGLIB$getName$0++++++
++++++before CGLIB$getAge$1++++++
getAge
------getAge------
++++++before CGLIB$getAge$1++++++
Copy the code

4, need to pay attention to the problem

It is important to note that when a method that is not transaction-wrapped by AOP calls another method that is transaction-wrapped by AOP inside that method, the AOP transactions for that method do not take effect. Such as:

public void register(a) {
    aopRegister();
}
 
@Transactional
public void aopRegister(a) {}Copy the code

As we know from the above analysis, in Spring, whether in JDK form or cGLIb form, the proxy class intercepts the methods of the target object by having the proxy class hold a reference to the target object. When external references to aOP-enclosed methods are invoked, the corresponding methods of the proxy class are actually called. The proxy class holds the target object and controls the full interception of the target method.

And if in the interior of the target method call register a surrounded by aop target aopRegister, call the method that is the target of itself, because at that time this pointer is impossible to point to the proxy class. So the transaction is not valid.

5. Advantages and disadvantages

  • Speed is
// Test results
// The speed of creating a proxy
Create JDK Proxy: 13 ms  
Create CGLIB Proxy: 217 ms  / / slowly
Create JAVAASSIST Proxy: 99 ms  
Create JAVAASSIST Bytecode Proxy: 168 ms   / / slowly
Create ASM Proxy: 3 ms  / / the fastest

================  
Run JDK Proxy: 2224 ms, 634.022 t/s  // Slow. The BYtecode generated by the JDK takes too many conditions into account, so there is a lot of bytecode, most of which is not needed
Run CGLIB Proxy: 1123 ms, 1.255.623 t/s  // Slower, also because there is a bit more bytecode
Run JAVAASSIST Proxy: 3212 ms, 438.999 t/s   // Very slow, and using Javassist's proxy classes for dynamic proxies is not recommended at all
Run JAVAASSIST Bytecode Proxy: 206 ms, 6.844.977 t/s  // Similar execution speed as ASM, because they only generate simple pure execution bytecode, very little (so the proxy class generation directly with Javassist is the most recommended approach [for speed])
Run ASM Bytecode Proxy: 209 ms, 6.746.724 t/s   // Asm is the fastest for everything. After all, it only deals with the lowest level
Copy the code
  • Convenience comparison

Sort (only generates bytecode, semantically): Javassist > ASM sort (dynamic proxy):cglib= JDK > Javassist Bytecode > Javassist Proxy > ASM