Detailed explanation of Java advanced Proxy dynamic Proxy mechanism

1, Jvm load object

Before talking about Java Dynamic Proxy, let's talk about the process of loading objects by Jvm. This is still the basic principle of understanding dynamic proxy:

java classes are source code programs java type files are converted into byte code after being compiled by the compiler Class type file. The class loader is responsible for reading byte code and converting it into java Lang. class object describes the data structure of the class in the metadata space. When the class is instantiated, the instantiated object information is stored in the heap, and the class is found through the pointer of the object type data.

Process Description: source code - > Java file - > Class file - > class object - > instance object

Therefore, you can create objects through New and dictate many implementation details behind them. After understanding the above process, you can understand a common design pattern, namely agent pattern.

2, Agent mode

1. Basic description

Proxy mode provides a proxy object to a (target) object, and the proxy object holds a reference to the target object. The so-called agent is that an object executes the corresponding action program on behalf of another object. The proxy object can act as an intermediary between the client and the target object.

The agency model has many scenarios in real life, such as intermediary, lawyer, purchasing agent and other industries. It is a simple agency logic. Under this model, there are two key roles:

Target object role: the object represented by the proxy object.

Proxy object role: contains a reference to the target object and can operate the target object; AOP programming is based on this idea.

2. Static and dynamic mode

  • Static agent: determine the agent role before the program runs, and clarify the relationship between the agent class and the target class.

  • Dynamic proxy: Based on Java reflection mechanism, proxy objects are dynamically created and generated at JVM runtime.

3, Static proxy

Based on the above concept of static agent, it is described and implemented with a piece of code. The basic logic is as follows:

  • Specify the target object, that is, the object to be represented;
  • Define the proxy object and hold the target object through the constructor;
  • Define pre and post enhancement methods in the proxy object;

The target object and the pre and post enhanced code form a proxy object, so there is no need to directly access the target object. It is very much like the sentence in a TV play: I am a lawyer, and my client is inconvenient to talk to you.

public class Proxy01 {
    public static void main(String[] args) {
        TargetObj targetObj = new TargetObj() ;
        ProxyObj proxyObj = new ProxyObj(targetObj) ;
        proxyObj.invoke();
    }
}
class TargetObj {
    public void execute (){
        System.out.println("Target class method execution...");
    }
}
class ProxyObj {
    private TargetObj targetObj ;
    /**
     * Hold target object
     */
    public ProxyObj (TargetObj targetObj){
        this.targetObj = targetObj ;
    }
    /**
     * Target object method call
     */
    public void invoke (){
        before () ;
        targetObj.execute();
        after () ;
    }
    /**
     * Pre post processing
     */
    public void before (){
        System.out.println("Proxy object preprocessing...");
    }
    public void after (){
        System.out.println("Post processing of proxy object...");
    }
}

Static proxy clearly defines the proxy object, that is, the object with one proxy object The process of loading java files into the JVM is obviously a problem. In the actual development process, it is impossible to define a proxy class for each target object, nor can one proxy object proxy multiple target objects. The maintenance costs of these two methods are very high.

The essence of proxy mode is to put enhanced operations before and after the methods of the target object, but do not want to modify the target Class. It can be known through the front reflection mechanism that the structure information of the object can be obtained during operation, and the proxy object can be dynamically created based on the Class information, which is the dynamic proxy mechanism.

By the way, it is well known that the underlying implementation logic of technology is not easy to understand. However, the basic knowledge points are not complex, such as the basic principle of agent mode, but when combined with practical complex applications (AOP mode), it is difficult to vividly understand that it is implemented based on reflection and dynamic agent.

4, Dynamic agent

1. Scene description

Describe the difference between dynamic agent and static agent based on a scenario, that is, the popular concept of overseas purchasing in recent years:

In the early days of the rise of purchasing agent, some people who often go abroad on business will meet the demand of purchasing agent, that is, the agent is fixed; Later, overseas purchasing platforms and a series of products such as Haitao emerged, that is, the user purchasing demand (target object) is realized by the purchasing platform, but the specific operation depends on real-time distribution. This scenario is similar to the principle of dynamic agent.

2. Basic API case

First, let's look at the two core classes. Here's a brief description of the concepts. After reading the basic process, we'll talk in detail:

  • Proxy - create proxy object, core parameters:

    • ClassLoader: (target class) loader;
    • Interfaces: (target class) interface array;
    • InvocationHandler: proxy calling mechanism;
  • InvocationHandler proxy class calling mechanism:

    • invoke: the principle of reflection mentioned in the previous article;
    • method: reflect the core API in the class library;

Target objects and interfaces

interface IUser {
    Integer update (String name) ;
}
class UserService implements IUser {
    @Override
    public Integer update(String name) {
        Integer userId = 99 ;
        System.out.println("UserId="+userId+";updateName="+name);
        return userId ;
    }
}

Proxy object execution mechanism

class UserHandler implements InvocationHandler {
    private Object target ;
    public UserHandler (Object target){
        this.target = target ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before()...");
        Object result = method.invoke(target, args);
        System.out.println("after()...");
        return result;
    }
}

Specific combination mode

public class Proxy02 {
    public static void main(String[] args) {
        /*
         * Generate class file for $Proxy0
         */
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        /*
         * Target object information
         */
        IUser userService = new UserService();
        ClassLoader classLoader = userService.getClass().getClassLoader();
        Class<?>[] interfaces = UserService.class.getInterfaces() ;
        /*
         * Create proxy object
         */
        InvocationHandler userHandler = new UserHandler(userService);
        /*
         * Proxy class object name
         * proxyClassName=com.java.proxy.$Proxy0
         */
        String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
        System.out.println("proxyClassName="+proxyClassName);
        /*
         * Specific business implementation simulation
         */
        IUser proxyUser1 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        IUser proxyUser2 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        proxyUser1.update("cicada") ;
        proxyUser2.update("smile") ;
    }
}

The reason why we want to generate the structure information of the proxy class here is that we can't see the relevant content from the loading process of the JVM, and the key information is arbitrary again:

javap -v Proxy02.class

View agent class name

/*
 * proxyClassName=com.java.proxy.$Proxy0
 */
String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
System.out.println("proxyClassName="+proxyClassName);

Subconsciously output the proxy object name, which corresponds to the JVM mechanism, find the Class object name, and then analyze the structure, so as to understand the specific execution principle of dynamic proxy.

Generate proxy classes class file

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

It can be seen from the above JVM object loading mechanism that the class object describing the proxy class must exist, but it does not generate an explicit object at runtime Class file, through which the proxy class is generated Class, the file will be created in the / com/java/proxy path of the project directory.

By the way: as a programmer, complexity always revolves around us. What about simplicity?

3. Proxy class structure

Inheritance and Implementation

class $Proxy0 extends Proxy implements IUser {}

Considering the functions of the Proxy class, we can think of the need to inherit the Proxy and implement the IUser interface, as well as the specific implementation class holding the call mechanism for business enhancement.

Construction method

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

Through the construction method, it holds the specific execution mechanism object of UserHandler.

Interface implementation

final class $Proxy0 extends Proxy implements IUser {
    private static Method m3;
    public final Integer update(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
}

The basic requirements of the target class are the update() method, which is undertaken through the proxy class, and the specific enhanced business processing is realized based on UserHandler.

Basic method

final class $Proxy0 extends Proxy implements IUser {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    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("com.java.proxy.IUser").getMethod("update", Class.forName("java.lang.String"));
            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 final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

Based on the Object class, define several common methods in Java, such as equals() judgment, toString() method and hashCode() value. When analyzing the Map source code, I said why these methods usually appear together.

4. JDK source code

The above is the process and principle of case execution. Another key point to understand is the logic of JDK source code:

IUser proxyUser = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);

The static method newProxyInstance() provided by Proxy constructs a new Proxy Class object, that is, the structure information of $Proxy0 Class, through the passing in of various parameters. Here, we look back at the following three core parameters:

  • ClassLoader: Based on the JVM running process, it is necessary to obtain the class loader of the target class UserService;

  • Interfaces: the interface implemented by the target class UserService. From the perspective of object-oriented, the interface is separated from the implementation. The proxy class simulates the requirements of the target class by implementing the IUser interface;

  • InvocationHandler: the function package provided by the proxy class, namely UserHandler, can be enhanced before and after the target method call;

Finally, it summarizes the core technical points of the implementation of dynamic agent: Jvm loading principle, reflection mechanism and object-oriented idea; Every time I read the source code of JDK, I will marvel at the uncanny workmanship of the designer. Only when I insist on dropping water through the stone can I gain.

JVM class loading mechanism | proxy pattern | AOP section programming | Custom logging | Map source code analysis

5, Source code address

GitHub·address
https://github.com/cicadasmile/java-base-parent
GitEE·address
https://gitee.com/cicadasmile/java-base-parent

Reading labels

[Java Foundation][Design mode][Structure and algorithm][Linux system][database]

[Distributed architecture][Microservices][Big data component][SpringBoot advanced][Spring & Boot Foundation]

[Data analysis][Technical map][ Workplace]

Tags: Java jvm Dynamic Proxy

Posted by God Ownz on Tue, 25 Jan 2022 14:56:23 +1030