Reprinted from: mp.weixin.qq.com/s/GT1-yrxJ5…

1. Proxy mode

Proxy mode is a design mode that provides additional access to target objects, that is, access target objects through proxy objects. In this way, additional operations can be provided to extend the functions of target objects without modifying the original target objects.

An example: when renting a house, some people will rent directly through the landlord, and some people will rent through the intermediary.

Which of these two situations is more convenient? It is more convenient to pass intermediary, of course.

The intermediary here is equivalent to the agent, through which users can complete a series of rental operations (house inspection, deposit, rental, cleaning and sanitation). The agent mode can effectively decouple the specific implementation from the caller, and completely hide the specific implementation internally through interface-oriented coding.

Classification:

Static proxy: Implemented at compile time, the proxy class is an actual class file after compilation.

Dynamic proxy: Dynamically generated at run time, that is, there is no actual class file after compilation, but instead the class bytecode is dynamically generated at run time and loaded into the JVM.

Static proxy

use

Create an interface, and then create proxied classes that implement that interface and implement abstract methods in that interface. Then create a proxy class that also implements this interface. Holds a reference to a proxied object in a proxy class, and then calls its methods in a proxy class method.

public interface UserDao {    
  void save(a);     
}
Copy the code
public class UserDaoImpl implements UserDao {
    @Override
    public void save(a) {
        System.out.println("Saving user..."); }}Copy the code
public class TransactionHandler implements UserDao {
    // Target proxy object
    private UserDao target;
    // Pass in the target object when constructing the proxy object
    public TransactionHandler(UserDao target) {
        this.target = target;
    }
    @Override
    public void save(a) {
        // Processing before calling the target method
        System.out.println("Turn on transaction control...");
        // Call the target object's method
        target.save();
        // Processing after calling the target method
        System.out.println("Turn off transaction control..."); }}Copy the code
public class Main {
    public static void main(String[] args) {
        // Create a target object
        UserDaoImpl target = new UserDaoImpl();
        // Create a proxy object and reference it with an interface
        UserDao userDao = new TransactionHandler(target);
        // Call the interfaceuserDao.save(); }}Copy the code

Proxying a class is easy with JDK static proxies. However, the disadvantages of JDK static proxies are also exposed: since the proxy can only serve one class, if there are many classes that need to be proxy, you need to write a large number of proxy classes, which can be tedious.

JDK dynamic proxy

Five steps to using JDK dynamic proxies:

  1. Customize your own InvocationHandler by implementing the InvocationHandler interface;
  2. throughProxy.getProxyClassObtain dynamic proxy classes;
  3. The constructor of the proxy class is obtained through reflection, and the method signature isgetConstructor(InvocationHandler.class);
  4. The proxy object is obtained through the constructor and will be customizedInvocationHandlerInstance objects are passed in as parameters;
  5. Call the target method through the proxy object;
public interface IHello {
    void sayHello(a);
}
Copy the code
public class HelloImpl implements IHello {
    @Override
    public void sayHello(a) {
        System.out.println("Hello world!"); }}Copy the code
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class MyInvocationHandler implements InvocationHandler {
 
    /** Target object */
    private Object target;
 
    public MyInvocationHandler(Object target){
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("------ Insert pre-notification code -------------");
        // Execute the corresponding target method
        Object rs = method.invoke(target,args);
        System.out.println("------ Insert post-processing code -------------");
        returnrs; }}Copy the code
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;

public class MyProxyTest {
    public static void main(String[] args)
            throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        / / = = = = = = = = = = = = = = = = = = = = = = = = = the first = = = = = = = = = = = = = = = = = = = = = = = = = =
        $Proxy0 = $Proxy0
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
        // get the dynamic proxy class
        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
        // get the constructor of the proxy class and pass in the argument type invocationHandler.class
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        // create a dynamic proxy object by using the constructor to pass in a custom InvocationHandler instance
        IHello iHello1 = (IHello) constructor.newInstance(new MyInvocationHandler(new HelloImpl()));
        Call the target method through the proxy object
        iHello1.sayHello();
 
        / / = = = = = = = = = = = = = = = = = = = = = = = = = = the second = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
        /** * There is an easy way to create a dynamic Proxy object that encapsulates steps 2 to 4. The method signature is newProxyInstance(ClassLoader,Class
      [] instance, InvocationHandler h) */
        IHello  iHello2 = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), // Load the class loader for the interface
                new Class[]{IHello.class}, // A set of interfaces
                new MyInvocationHandler(new HelloImpl())); // Custom InvocationHandleriHello2.sayHello(); }}Copy the code

JDK static proxies are somewhat similar to JDK dynamic proxies in that they both create proxy classes and implement interfaces.

The difference: in a static proxy, we need to create a proxy class for which interface and which proxied class, so we need the proxy class to implement the same interface as the proxied class before compilation, and directly call the proxied class’s corresponding method in the implemented method; Dynamic proxies, however, are different. We do not know for which interface or proxied class the proxy class will be created, because it is created at run time.

To summarize the differences between JDK static proxies and JDK dynamic proxies:

JDK static proxies are created by direct encoding, whereas JDK dynamic proxies use reflection to create proxy classes at run time.

At the heart of the dynamic proxy is the InvocationHandler. Each instance of a broker has an associated InvocationHandler. When a call is made to a proxy instance, the call to the method is encoded and assigned to the invoke method of its InvocationHandler.

Invocation of a proxy object instance method is done through the Invoke method in InvocationHandler, which determines which method to invoke based on the passed proxy object, method name, and parameters.

4, additional

At the bottom of the CGLIB package is the ability to transform bytecode and generate new classes by using ASM, a small and fast bytecode processing framework.

CGLIB proxy implementation is as follows:

  1. We first implement a MethodInterceptor, and the method calls are forwarded to the Intercept () method of that class.
  2. The proxy object is then retrieved through the CGLIB dynamic proxy when needed.

Use case

 public class HelloService {
 
    public HelloService(a) {
        System.out.println("HelloService structure");
    }
 
    /** * This method cannot be overridden by subclasses. Cglib is not a proxy for final modified methods
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }
 
    public void sayHello(a) {
        System.out.println("HelloService:sayHello"); }}Copy the code
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
import java.lang.reflect.Method;
 
/** * Custom MethodInterceptor */
public class MyMethodInterceptor implements MethodInterceptor{
 
    /** * sub: cglib generated proxy object * method: proxied object method * objects: method entry * proxy: proxy method */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("====== Insert pre-notification ======");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("====== insert the latter notification ======");
        returnobject; }}Copy the code
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
 
public class Client {
    public static void main(String[] args) {
        // The proxy class file is stored in the local disk to facilitate the decompression of the source code
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        // The process of obtaining proxy objects through CGLIB dynamic proxies
        Enhancer enhancer = new Enhancer();
        // Set the enhancer object's parent class
        enhancer.setSuperclass(HelloService.class);
        // Set the enhancer callback object
        enhancer.setCallback(new MyMethodInterceptor());
        // Create a proxy object
        HelloService proxy= (HelloService)enhancer.create();
        // Call the target method through the proxy objectproxy.sayHello(); }}Copy the code

JDK proxies have strong limitations by requiring propped classes to implement interfaces.

CGLIB dynamic proxies have no such mandatory requirements. In simple terms, CGLIB makes the generated proxy class inherit from the proxied class and enhances the proxy methods in the proxy class (pre-processing, post-processing, and so on).

Summarize what CGLIB does when it comes to proxying

  • The generated proxy class inherits the proxied class. One thing to note here is that if a delegate class is final, it cannot be inherited, that is, it cannot be proxied; Similarly, if a final modified method exists ina delegate class, that method cannot be proxied.
  • The proxy class generates two methods for the delegate method, one with the same signature as the delegate method, which passes in the methodsuperCall the delegate method; The other is a method unique to the proxy class.
  • When executing a method on a proxy object, it first determines whether an implementation existsMethodInterceptorOf the interfaceCGLIB$CALLBACK_0; , if it exists, is calledMethodInterceptorIn theinterceptMethods.

In the Intercept method, in addition to calling the delegate method, we also make some enhancements. In Spring AOP, a typical application scenario is operation logging before and after some sensitive method execution.

In CGLIB, methods are called not by reflection, but directly by methods: Class objects are treated in special ways through the FastClass mechanism, such as holding a reference to a method in an array and holding the reference to the method with an index subscript each time a method is called.

5. Fastclass mechanism

CGLIB uses the FastClass mechanism to call intercepted methods.

The FastClass mechanism is to index a class’s methods and use the index to call the corresponding methods directly.

public class test10 {
  // Here, tt can be regarded as the target object, fc can be regarded as the proxy object; We first get the index of the target method based on the proxy object's getIndex method,
  // Invoke the proxy object's invoke method to invoke the target class directly, avoiding reflection
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null); }}class Test{
    public void f(a){
        System.out.println("f method");
    }
    
    public void g(a){
        System.out.println("g method"); }}class Test2{
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    // This method indexes methods in the Test class
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1; }}Copy the code

In the above example, Test2 is the Fastclass of Test, and there are two methods getIndex and invoke in Test2.

Index each method of Test in the getIndex method and return the corresponding index based on the input parameter (method name + method descriptor).

Invoke invokes a method on object O with ol as an input parameter, based on the specified index. This avoids reflection calls and improves efficiency.

Compare the three methods of agency

6, problem,

CGlib faster than JDK?

  • Using CGLiB to achieve dynamic proxy, CGLiB uses ASM bytecode generation framework, using bytecode technology to generate proxy class, before JDK6 than using Java reflection efficiency. The only caveat is that CGLib cannot proxy methods that are declared final, because CGLib’s principle is to dynamically subclass the proxied class.
  • After JDK dynamic proxy optimization in JDK6, JDK7, and JDK8, JDK proxy efficiency is higher than CGLIB proxy efficiency in the case of fewer calls. Jdk6 and JDK7 are only slightly less efficient than CGLIB proxies when making a lot of calls, but by JDK8 the JDK proxies are more efficient than CGLIB proxies. Overall, JDK proxies get more efficient with every JDK version upgrade, while CGLIB proxy messages do keep up a bit.

How does Spring choose to use JDK or CGLIB?

  • Spring uses the JDK’s dynamic proxy when the Bean implements the interface.
  • When a Bean does not implement an interface, Spring uses the CGlib implementation.
  • You can force the use of CGlib.

Personal website

  • Github Pages
  • Gitee Pages