头图

Preface

In the first article how to implement AOP (on) introduced the AOP technology and some important concepts, before we implement it ourselves, it is necessary to understand how the AOP actually works, so this article will look at the implementation of AOP Some core basic technologies on which it depends. AOP is implemented using dynamic proxy and bytecode generation technology. In the runtime (note: not at compile time!), a proxy object is generated for the target object, and then the crosscutting logic is woven into the generated proxy object In the end, the system uses proxy objects with cross-cutting logic instead of the proxy objects, which are forwarded from the proxy object to the proxy object.

Agency model

The root of the dynamic proxy is the proxy mode in the design pattern. The proxy mode is described in GoF as follows:

Provide a surrogate or placeholder for another object to control access to it.

It can be seen from its definition that the proxy mode is mainly to control the access of the object, and usually also has all the functions of the proxy. Through the proxy mode, we can add some functions to the proxy class by introducing the proxy class without changing the proxy class. At this time, a famous sentence in the computer science community floats in our mind (PS basic knowledge is very important, yes. See this ):

Any problem in computer science can be solved by adding an intermediate layer.

In fact, the agency model is often encountered in real life. For example, when renting a house in first-tier cities, most of them are renting agencies to look at the house, negotiate the price, and sign the contract, because the landlord of the house has fully entrusted the house to it. The intermediary dealt with it. The renting intermediary here actually acts as a solution to the agency object in the agency model, and the real agent (target object) is actually the landlord of the house, and we are all renting intermediaries (agent objects) when dealing with us. The structure of the proxy model class diagram is shown in the following figure:

proxy-pattern.png

The meaning of each part in the figure is as follows:

  • ISubject is an abstraction of the capabilities provided by the proxy object.
  • SubjectImpl is the specific implementation class of the agent.
  • SubjectProxy is a proxy implementation class, usually this class holds an instance of the ISubject
  • Client is the abstract role of the visitor, who wants to access ISubject type 060b338016d231.

You can see SubjectImpl and SubjectProxy implement the same interface ISubject , the proxy object SubjectProxy held within ISubject referenced when Client access doOperation() , the proxy object forwards the request to the proxy object, just from this process, the proxy object If it’s just for forwarding the request, isn’t it a bit redundant? Consider again with the definition of the proxy mode, can't you add some access control before forwarding (after the latter).

static-proxy.png

The proxy object ( SubjectProxy ) forwards the request to the proxy object ( SubejctImpl before or after) or add some processing logic is required, without the need to modify the proxy logic is embodied in an object, assuming SubjectImpl our system Joinpoint object is located , At this time SubjectImpl is our target object, we only need to create a proxy object for this target object, and then add the crosscutting logic to the proxy object, and expose the created proxy object to the outside to change the crosscutting logic to the original The logic is merged together.

Dynamic proxy

So far, everything is so beautiful. When only adding cross-cutting logic to the same target object type, you only need to create a proxy object, but when the Joinpoint same but the target object type is different, you need to create a different The target object type creates a proxy object separately, and the cross-cutting logic of these proxy objects is actually the same. According to the DRY principle , it is necessary to find another technology to solve this problem.
In JDK 1.3 introduced dynamic proxy mechanism for the specified interface dynamic runtime to generate the proxy object, use this dynamic proxy mechanism can solve these problems, so we can not create a proxy class in advance for each original class, Instead, the proxy class is dynamically generated In Java , the use of dynamic proxy is relatively simple, it has already used reflection to implement the syntax of dynamic proxy, which is mainly composed of a class Proxy and an interface InvocationHandler . The previous example is implemented using dynamic proxy mechanism as follows:

/**
 * @author mghio
 * @since 2021-05-29
 */
public interface Subject {
  void doOperation();
}

public class SubjectImpl implements Subject {
  @Override
  public void doOperation() {
    System.out.println("SubjectImpl doOperation...");
  }
}

public class JDKInvocationHandler implements InvocationHandler {
  private final Subject target;
  public JDKInvocationHandler(Subject target) {
    this.target = target;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // add pre process logic if necessary
    
    System.out.println("Proxy before JDKInvocationHandler doOperation...");
    Object result = method.invoke(target, args);
    System.out.println("Proxy after JDKInvocationHandler doOperation...");

    // add post process logic if necessary
    return result;
  }
}

public class Client {
  public static void main(String[] args) {
    // 将生成的代理类保存在根目录下(com/sun/proxy/XXX.class)
    System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    Subject target = new SubjectImpl();
    Subject proxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(),
        new Class[]{Subject.class}, new JDKInvocationHandler(target));
    proxy.doOperation();
  }
}

As can be seen from the above code, it only JDK 3 steps to use the dynamic proxy of 060b338016d394:

  1. Define the public interface Subject and create the proxy object SubjectImpl
  2. Create the processing object JDKInvocationHandler proxied object, holding a reference to the Subject
  3. Use the static method JDK of the Proxy newProxyInstance create a proxy object

By setting the sun.misc.ProxyGenerator.saveGeneratedFiles attribute, you can save the dynamically generated proxy class in the project root directory. The proxy class generated by running the above sample code is as follows:

public final class $Proxy0 extends Proxy implements Subject {
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
      m2 = Class.forName("java.lang.Object").getMethod("toString");
      m3 = Class.forName("cn.mghio.designpattern.proxy.dynamicproxy.Subject").getMethod("doOperation");
      m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
      throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
      throw new NoClassDefFoundError(var3.getMessage());
    }
  }

  public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
  }

  public final void doOperation() throws  {
    try {
      super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }

  // omit toString、equals、hashCode method...

}

It can be seen from the code of the dynamically generated proxy class that the proxy class generated by the JDK dynamic proxy inherits the JDK provided in Proxy and implements the interface implemented by the proxy class. Two further points can be drawn from this implementation: 1. When using JDK dynamic proxy, why can only use the interface reference to point to the proxy, but not the specific class reference of the proxy to point to the proxy; 2. The proxy class must implement the interface, because The proxy class generated by the dynamic proxy of JDK Proxy , and Java does not support multiple inheritance, so it can only be achieved through the interface.

jdk-dynamic-proxy.png

By default, Spring AOP finds that the target object implements the interface, and will use the JDK dynamic proxy mechanism to dynamically generate proxy objects for it. Although for interface-oriented programming, there are also scenarios where the target object does not implement the interface. If the interface is not implemented, the JDK dynamic proxy cannot be used. In this case, you need to use a third-party tool to help.

Bytecode generation technology

When the target object does not implement the interface, dynamic bytecode generation can be used to inherit the target object to dynamically generate the corresponding subclass. In the generated subclass, overrides the behavior of the parent target object, and then puts the crosscutting logic in Subclass, using the subclass of the target object in the system, the final effect is that the proxy mode is the same, CGLIB dynamic bytecode generation library (it is actually an abstraction layer, and the lower layer is ASM ). Dynamically generate and modify the bytecode of a class. When the target object of the above sample code does not implement the interface, it is modified to CGLIB dynamically generate bytecode to achieve the following:

/**
 * @author mghio
 * @since 2021-05-29
 */
public class RealSubject {
  public void doOperation() {
    System.out.println("RealSubject doOperation...");
  }
}

public class CglibMethodInterceptor implements MethodInterceptor {
  @Override
  public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
      throws Throwable {
    // add pre process logic if necessary

    System.out.println("Cglib before RealSubject doOperation...");
    Object result = methodProxy.invokeSuper(o, args);
    System.out.println("Cglib after RealSubject doOperation...");

    // add post process logic if necessary
    return result;
  }
}

public class Client {
  public static void main(String[] args) {
    // 将生成的动态代理类保存到文件中
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
        "/Users/mghio/IdeaProjects/designpattern/cglib/proxy/");
    // 1. 创建 Enhancer 对象
    Enhancer enhancer = new Enhancer();
    // 2. 设置被代理类
    enhancer.setSuperclass(RealSubject.class);
    // 3. 设置回调对象(实现 MethodInterceptor 接口)
    enhancer.setCallback(new CglibMethodInterceptor());
    // 4. 创建代理对象
    RealSubject proxy = (RealSubject) enhancer.create();
    proxy.doOperation();
  }
}

Using CGLIB generate a proxy object requires 4 steps:

  1. Create the Enhancer object, and most of the logic of dynamically generating bytecode is done in this class.
  2. Set the proxy class (target object), the generated proxy class will inherit from this interface, which means that this class cannot be of type final
  3. Set the callback object (implement the MethodInterceptor interface), and add crosscutting logic here as needed.
  4. Call Enhaner of create() way to create a proxy object.

After setting the DebuggingClassWriter.DEBUG_LOCATION_PROPERTY attribute, decompile the saved dynamically generated proxy class as follows:

cglib-generate.png

From the decompiled code can be seen CGLIB generated proxy class is inherited by a proxy class RealSubject achieved Factory interface, to be inherited proxy class will not be required to be final type. Seeing this, you may ask: Since the JDK dynamic proxy requires the proxy class to implement the interface, and the CGLIB dynamic bytecode generation requirement cannot be the final class, then for those who do not implement the interface and are still final , how to dynamically proxy? Good question, this is left to you to think about.

to sum up

This article briefly introduces the Spring AOP depends, from the root proxy mode of dynamic proxy to dynamic proxy and dynamic bytecode generation technology, and AOP next article. After understanding the basis on which it depends After the technology, the specific implementation will be more silky. The dynamic proxy and the dynamic bytecode generation is as follows:

Comparison itemJDK dynamic proxyCGLIB
Ways to generate proxy classesInherit JDK in Proxy and implement all interfaces of the proxied classInheritance is a proxy class that implements CGLIB of Factory Interface
Requirements of the proxy objectThe interface must be implemented, which can be of type finalNon- final type, the method should also be non- final type
Integration methodJDK built-inThird-party dynamic bytecode generation library

mghio
446 声望870 粉丝