From 0 to 1! Dynamic and static agent

trace back to

To learn a technology, we need to know why it comes into being, so that we can have the goal and motivation to learn and understand it better

First of all, why should there be an agent?

There is A common requirement: how to add and enhance some functions when calling methods of class A without modifying class A code?

Without considering what agents are not, we design a simple implementation scheme:

A new class B, class B combination class A is created, a method B is created in class B, method a in method A is invoked in method B, and some custom add and enhancement codes can be added before and after invoking. When there is a need to call method a of class A and want to add an additional function, call method B of class B to achieve the above requirements;

For ease of understanding, the following pseudo code is attached:

// Definition class A
public class  ClassA{
    public void methoda(){
       System.out.println("I'm the way a!");
    }
}

// Definition class B
public class ClassB{
    // Combination ClassA
    ClassA  A;
    public ClassB(ClassA A){
        this.A = A;
    }

    @Override
    public void methodb(){
        System.out.println("I'm the way a Additional function code of, I executed~!");
        A.methoda();
        System.out.println("I'm the way a I'm finished~!");
    }
}

Next, let's call the methodb method of ClassB to generate the following output:

I am the additional function code of method a, I execute ~!
I'm method a!
I am the additional function code of method a, I have finished ~!

It can be found that method a executes and adds other functions to method a without modifying class a code;
It's not hard. In fact, the above code is the simplest agent mode

The significance of agent existence: using agent mode, you can add and enhance some functions by extending agent classes without modifying other agent object codes

Type of agency

Agents are divided into static agents and dynamic agents. The design mode involved is the agent mode itself. The agent mode generally includes several elements, as shown in the following figure:

  1. Subject: a public external method that defines a proxy class and a real subject. It is also a method that a proxy class represents a real subject;
  2. Real subject: a class that truly implements business logic;
  3. Proxy class: used to proxy and encapsulate real themes;
  4. Client: use the proxy class and topic interface to do some work.

In order to better understand, we will improve the simplest version of the agent implemented above, add an interface, and the agent class will also implement the corresponding interface of the proxied class, and implement the same method. The pseudo code is as follows:

// The interface of the proxy class (subject in the figure above)
public interface ImpA{
      void methoda();
}
// Definition class A (RealSubject in the above figure)
public class  ClassA implements ImpA{
    public void methoda(){
       System.out.println("I'm the way a!");
    }
}

// Definition class B (Proxy in the above figure)
public class ClassB implements  ImpA {
    // Combination ClassA
    ImpA  A;
    public ClassB(ClassA A){
        this.A = A;
    }

    // Override the method of the proxy class
    @Override
    public void methoda(){
        System.out.println("I'm the way a Additional function code of, I executed~!");
        A.methoda();
        System.out.println("I'm the way a I'm finished~!");
    }
}
// Client class (client in the figure above)
public class Main{
    // Create proxy object instance
    ImpA A = new ClassA();
    // Constructor injection proxy object instance
    ImpA B = new ClassB(A);
    // Call proxy method
    B.methoda();
}

Static agent

The so-called static agent is that the bytecode file of the agent class already exists before the program runs, and the relationship between the agent class and the delegate class is determined before the program runs.
The above code is to implement a static agent. In fact, the static agent can meet the above requirements. Why do we need a dynamic agent? Here are two disadvantages of static agents

  1. An interface of proxy object only serves one type of object. If there are many methods to proxy, it is bound to proxy every method. When the program scale is a little large, too many static proxy classes will cause code confusion
  2. If a method is added to the interface, in addition to all implementation classes that need to implement this method, all agent classes also need to implement this method, which increases the complexity of code maintenance.
    Based on the above two problems, dynamic agent is born~

Dynamic agent

Dynamic proxy is to obtain bytecode content of the proxied class through reflection when the program is running to create a proxy class

What is dynamic agent?
Noun: dynamic, dynamic in a program is to express the automatic generation of agent classes according to configuration when the program is running, and the relationship between agent classes and proxied classes is determined only when they are running;

In JDK, there are two implementation mechanisms of dynamic proxy: JDK Proxy and CGLib;

Let's take JDK Proxy as an example to explain the dynamic proxy and the application scenario based on the source code analysis

JDK Proxy

JDK Proxy dynamic proxy, api in package java.lang.reflect Next, you may find out, why is it under the reflection package? The following source code analysis will solve this problem;

Its core api includes two important core interfaces and classes: one is the InvocationHandler(Interface) and the other is the Proxy(Class). Simply speaking, these two are very simple. These two are necessary for us to implement dynamic proxy. Here are two classes:
java.lang.reflect.Proxy (Class): Proxy is the main Class of Java dynamic Proxy mechanism. It provides a set of static methods to dynamically generate Proxy classes and their objects for a set of interfaces. There are four static methods:

  • static InvocationHandler getInvocationHandler(Object proxy)
    This method gets the call handler associated with the specified proxy object
  • static Class getProxyClass(ClassLoader loader, Class[] interfaces)
    This method is used to get the class object of the dynamic proxy class associated with the specified class loader and a set of interfaces
  • static boolean isProxyClass(Class cl)
    This method is used to determine whether the specified class object is a dynamic proxy class
  • static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
    This method is used to generate a dynamic proxy class instance for the specified class loader, a set of interfaces, and the calling processor. It contains the following parameters:
    • Loader specifies ClassLoader loader of agent class
    • Interfaces specifies all interfaces to be implemented by the proxy class
    • h: Indicates which InvocationHandler object the dynamic proxy object will be associated with when calling a method

This method is used to generate a dynamic proxy class instance for a specified class loader, a set of interfaces, and a calling processor

java.lang.reflect.InvocationHandler (Interface): the InvocationHandler is the input of the InvocationHandler h parameter of the newProxyInstance method, which is responsible for connecting the interface that must be implemented by the intermediate class of the proxy class and the delegate class
It defines an invoke method, which is used to process the method calls on the dynamic proxy class object, and usually implements the proxy access to the proxy class in this method.

These are the two core methods of dynamic agent. Don't you understand? First, don't worry. Let's implement a dynamic agent with the above. Let's have a look

In the above case, the static agent is transformed into the dynamic agent. There are two steps to realize the dynamic agent, assuming that the ImplA and ClassA mentioned above still exist

1: Create a processor class to implement the InvocationHandler interface and override the invoke method. The pseudo code is as follows:

public class MyHandler implements InvocationHandler{
    // Identify the instance object of the proxied class
    private Object delegate;   
    // Constructor injection of proxied object
    public MyHandler(Object delegate){
       this.delegate = delegate;
    }

    // Override invoke method
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Additional code execution before called by proxy method~ ");
        // Real proxy method call
        method.invoke(delegate, args);
        System.out.println("Additional code execution after being called by proxy method~ ");
    } 
}

Well, such a processor is done. When we call the method of the proxy class, we are going to execute the above rewritten invoke method. Next, create a proxy class of ClassA

2: Create a proxy class and call the proxied method

public class MainTest{
    public static void main(String[] args) {    
        // Create proxied object
        ImplA A = new ClassA();
        // Create processor class implementation
        InvocationHandler myHandler = new MyHandler(A);
        // A key! Generate proxy class, where proxyA is the proxy class of A
        ImplA proxyA = (ImplA)Proxy.newProxyInstance(A.getClass().getClassLoader(), A.getClass().getInterfaces(), myHandler);
        // To call the method a method of the agent of the agent class, you will call the invoke method area of myHandler above to execute. As for why, leave the question first, and we will make it clear below~
        proxyA.methoda();
    }
}

Now, a dynamic agent has been built. After executing the code, you will find the output:

Additional code execution before called by proxy method~
I'm method a!
Additional code execution after being called by proxy method~

It's too simple. Here's a summary of the advantages and disadvantages of dynamic agents:

advantage:

  1. Dynamic proxy class not only simplifies the programming work, but also improves the scalability of the software system, because Java reflection mechanism can generate any type of dynamic proxy class.

  2. The bytecode of the dynamic proxy class is generated dynamically by the Java reflection mechanism when the program is running, so the programmer does not need to write its source code manually.

  3. The interface adds a method. In addition to all implementation classes that need to implement this method, the dynamic proxy class will automatically generate the corresponding proxy method directly.

Disadvantages:
JDK proxy can only proxy classes with implementation interfaces, that is to say, classes without interface implementation cannot be proxied by JDK proxy. Why? The following will be answered

Is there a solution? Of course, there is also a dynamic proxy scheme: CGLib, which implements proxy for a class. It mainly generates a subclass for a specified class, covers the methods in it, and covers the method implementation enhancements in it. However, because inheritance is adopted, it is better not to declare the class or method as final. For a final class or method, it cannot be inherited, and JDK The basic idea of proxy is similar. After all, it's the implementation scheme of dynamic proxy. I won't explain it in detail in this article. The blogger will introduce the framework of nb separately in other blogs

In the process, have you ever thought about how to realize dynamic agent?

Let's analyze from the source point of view to solve your questions.

Source code analysis

At the beginning of the analysis, I hope you can read with a few questions to help you understand better:

  • Question 1: why can proxy classes be generated automatically at runtime? How is it generated?
  • Question 2: why can the corresponding proxy method of the proxy class be called to the invoke method of the InvocationHandler implementation class?
  • Question 3: why does jdk proxy only support classes with interface implementation?

ps: in order to improve the reading experience and let everyone have a clearer understanding, the following source code will delete some exception handling and log printing codes, and only keep the trunk code, please be aware~

We analyze the whole process from two cores: InvocationHandler and Proxy. They are java.lang.reflect Under the bag

InvocationHandler source code

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

The above is the source code of InvocationHandler. Nothing else is an interface. There is a method to be implemented in it, invoke. The processing class implements this interface and rewrites the invoke method

Proxy source code

public class Proxy implements java.io.Serializable {
    // Handling class instance variables
    protected InvocationHandler h;
    // Used to store proxy class cache that has been obtained through dynamic proxy
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  proxyClassCache = new WeakCache<>(new KeyFactory(),new ProxyClassFactory());
    // Private parameterless construction, so that the object can only be created by passing in the InvocationHandler parameter
    private Proxy() {}
    // Protection constructor, enter the InvocationHandler processing class
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces) throws IllegalArgumentException{
        ...
    }
    public static boolean isProxyClass(Class<?> cl) {
        ...
    }
    public static InvocationHandler getInvocationHandler(Object proxy)  throws IllegalArgumentException {
        ...
    }
    // Implementation of generating agent class
    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
        ...
    }
    // Various private methods
    private ... ...
}

The overall architecture of the Proxy class is similar to the above. The InvocationHandler h parameter, two constructors, four common methods as described above, and a series of private methods are included. getProxyClass, isProxyClass and getInvocationHandler functions are the same as those described above, so we will not introduce them in detail

Let's take a look at the newProxyInstance method
For the newProxyInstance method, I added the corresponding comments in the method:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{
        // 1. Clone the corresponding interface for the interface array implemented by the proxy class
        final Class<?>[] intfs = interfaces.clone();
        ...
        /*
         * Look up or generate the designated proxy class.  Introduction in source code
         * 2. Find or generate the specified proxy class, which will be described in detail below
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.
         * 3. The above code has generated the proxy class cl, which contains a constructor whose parameter is the incoming InvocationHandler h, gets the constructor and creates an instance object of the class through the constructor and returns
         */
        try {
            // 4. Get the constructor whose parameter is InvocationHandler through reflection
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 5. Judge whether the constructor is private. If it is private, you need to set the private access permission
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 6. Create the corresponding instance object through the constructor obtained above, and return! over~
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
           // Various exception handling
        }
    }

In the above code, I simply annotated the function of each line of code. Let's analyze it in detail;

Bytecode generation logic of agent class

We know that before loading the jvm, java files have been compiled into class bytecode files, and then the jvm loads the bytecode files into the jvm through the class loader;

Our proxy class is the same. The difference is that the dynamic proxy class is generated when the program is running. What we have to do is how to generate the proxy class bytecode through the bytecode of the proxy class when the program is running!

Let's analyze the newProxyInstance method in detail:

In newProxyInstance, we call Class< > CL = getProxyClass0 (loader, intfs); the statement generates the bytecode of the proxy class, where the getProxyClass0 method is invoked, and the specified class loader and the corresponding interface to be implemented are imported.

Then, let's see the implementation of getProxyClass0 method:

    private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // proxyClassCache is a WeakCache weak reference cache class. If the corresponding proxy class has been generated before, it will be taken from the cache. If it has not been generated, it will be regenerated
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache is a static variable in the Proxy class and a WeakCache class. It encapsulates two classes, KeyFactory and ProxyClassFactory, which are bifunctional functional interfaces (if you are not clear about the functional interfaces, Google yourself);

Take it over and look at proxyClassCache = new WeakCache< > (New KeyFactory (), new ProxyClassFactory ()), where it is called. proxyClassCache.get Implementation of (loader, interfaces) method

The code is not too long, only the core code is pasted:

   public V get(K key, P parameter) {
        ...
        // This part mainly obtains the corresponding functional interface. If you don't understand the functional interface, google it~
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        while (true) {  // Why is the while loop here? If the supplier is not empty, then the next time you execute the following statement assignment, the next time you execute the recycle, the supplier is not empty
            if (supplier != null) {
                // If there is a corresponding functional interface, call the code corresponding to the functional interface
                // a key!!! Call function interface!!
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            if (factory == null) {
                // Create a factory class that specially creates the bytecode of the proxy class. The implementation class is ProxyClassFactory
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // Assign supplier to factory
                    supplier = factory;
                }}}} }

Summarize the process of the above methods:

next ProxyClassFactory.apply Here's how:

       public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            // Get the interface class object corresponding to the interface
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } 
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { }
            }

            String proxyPkg = null;
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            // Determine whether the public interface object is included, and whether the proxy class can be generated by jdk proxy
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                    }
                }
            }
            // If there is no public interface class, you need to use CGLib to implement...
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            // Class name of assembly agent class
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            // a key!! The bytecode array of the proxy class is generated here
            byte[] proxyClassFile = ProxyGenerator.generateProxyClassproxyName, interfaces, accessFlags);
            try {
               // The bytecode array is loaded into the method area of the JVm through the Class loader to generate the Class object!
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
            }
        }

The above byte [] proxyclassfile= ProxyGenerator.generateProxyClass (proxyName, interfaces, accessFlags); in order to generate the proxy class byte code array, the generateClassFile method is invoked in the method called.

private byte[] generateClassFile() {
        // First, there are three methods for the default agent: hashCode\equals\toString
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        // Get all interfaces to be implemented by proxy class
        Class[] var1 = this.interfaces;
        int var2 = var1.length;
        int var3;
        Class var4;
        // Traverse the interface obtained above
        for(var3 = 0; var3 < var2; ++var3) {
            // Assign: assign the Class object of the interface!
            var4 = var1[var3];
            // Get all methods by reflection
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                // Add method to method to be proxied
                this.addProxyMethod(var8, var4);
            }
        }
        // After getting the method to be proxy, start assembling bytecode
        var14.writeInt(-889275714);
        var14.writeShort(0);
        var14.writeShort(this.accessFlags);
        var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
        // be careful!!! ...... The assembly process of most bytecodes is omitted here. Only a few lines of code are provided to show the assembly of bytecodes
        // Finally return the assembled bytecode file
        return var13.toByteArray();
        }
    }

In generateClassFile, you will find that it is all the code of recombining bytecode, It is mainly to get the bytecode of the proxy class and the bytecode of the operation class InvocationHandler to assemble the bytecode of the proxy class. In the process of reorganization, because the proxy class is created at runtime, it is impossible to get an instance of the proxy class to get its method and let the proxy class call as usual.

After getting the bytecode, we will load the bytecode of the proxy class into the JVM. Here we call a return defineClass0 (loader, proxyname, proxyclassfile, 0, proxyClassFile.length )defineClass0 is a local native method, passing in the proxy class name, class loader, bytecode file and file length parameter of the proxy class, so as to load the bytecode into the JVM! The code is as follows:

private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);

After loading the bytecode of the proxy Class into the JVM, a Class object will be generated in the method area to identify the proxy Class;

Instance generation logic of agent class
Above, we know that bytecode of proxy Class is generated by bytecode technology, and bytecode file is loaded into the method area of JVM by classloader to generate a Class object. How can we obtain an instance of this Class object at runtime? Can only be used if the object instance is obtained no~
Or go back to the newProxyInstance method. We analyzed the logic of Class & lt;? & gt; CL = getproxyclass0 (loader, intfs) and generated the Class object cl. the next birth is the instance code. The process is very simple. I annotated the relevant logic directly in the code

    // Define the parameter type of the constructor. The following statement uses the
    private static final Class<?>[] constructorParams =    { InvocationHandler.class };

    // Obtain the parameter constructor of the Class object obtained above through reflection. The parameter must be defined above InvocationHandler.class type
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    // Check permissions, if private, set to accessible
    if (!Modifier.isPublic(cl.getModifiers())) {
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
               public Void run() {
                   cons.setAccessible(true);
                    return null;
                }
          });
     }
     // Pass in the corresponding processing class h parameter through the constructor to generate an instance!
    return cons.newInstance(new Object[]{h});

The above is the code to generate the instance. After the instance is generated, newProxyInstance will return to the instance, and then it can be used~

Reflection: get bytecode of the proxied class at run time

How can we get the bytecode of the constructor, method and property of the proxy class at runtime? Now "reflect!" On the stage! We can get all the information of the class at run time through reflection, all.
Definition: java reflection mechanism is a reflection mechanism of java language, which can know all the properties and methods of any class in the running state; can call any method and property of any object; this function of dynamically obtaining information and dynamically calling object methods is called java reflection mechanism.

For example, when assembling the bytecode of the proxy class as mentioned above, Method[] var5 = var4.getMethods() is called when all the methods of the proxy class are obtained; getMethods method in reflection obtains all methods of the proxy class through reflection, so that we can get all bytecode information of any class at runtime! Thus we can assemble the bytecode of the proxy class we want!

So reflection also provides theoretical support for the implementation of dynamic agent!! Because only when the information of the corresponding class can be obtained at runtime, can we create the corresponding proxy class we need through the information;

Source code analysis summary

In a word, the theoretical support of dynamic agent is that all the information of the class can be obtained at run time through reflection mechanism. If the information of the class being proxied cannot be obtained at run time, how to generate the agent class.

General process of dynamic agent:

Through the above process. We get a proxy class object. When we call the method corresponding to the proxy class, we will execute the execution logic specified by us and realize the runtime dynamic enhancement and expansion of the proxy class!

At this point, we will take out the code that generated the proxy class in the dynamic proxy code that we implemented with JDK proxy at the beginning: impla proxya = (impla) Proxy.newProxyInstance (a.getclass(). Getclassloader(). A.getclass(). Getinterfaces(). Myhandler) is the function of each parameter clear

Q & A

In the above dynamic agent implementation process, we can answer why the first agent class can be generated automatically when running? How is it generated? There's a problem

For the second reason, why can the corresponding proxy method of the proxy class be called to the invoke method of the InvocationHandler implementation class? And third, why does jdk proxy only support classes with interface implementation? Problem, we need to decompile the proxy class generated by bytecode technology as follows:

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.test.ImplA").getMethod("methoda");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
           // ..
        }
    }

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    // Method a to be strengthened
    public final void methoda() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean equals(Object var1) throws  {
        // Omit some code...
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    }
    public final String toString() throws  {
        // Omit some code...
        return (String)super.h.invoke(this, m2, (Object[])null);
    }
    public final int hashCode() throws  {
        // Omit some code...
        return (Integer)super.h.invoke(this, m0, (Object[])null);
    }
}

The above code contains several key points:

  1. Method is of type final and can no longer be inherited
  2. Proxy name is $proxy proxy class prefix + increasing number
  3. It inherits the core class Proxy of dynamic Proxy and implements the interface ImplA we specified
  4. A construction method with parameters $Proxy0(InvocationHandler var1) passed in InvocationHandler, which internally called the constructor of the Proxy of the parent class
  5. Method a, toString, hashCode and equals all call the invoke method of the incoming InvocationHandler parameter!!!

Now answer the second question: why can the corresponding proxy method of the proxy class be called to the invoke method of the InvocationHandler implementation class?

Obviously, all the proxy methods inside the proxy class explicitly call the InvocationHandler to implement the invoke method of the class

Third, why does jdk proxy only support classes with interface implementation?

Because Proxy class inherits Proxy class by default when using JDK proxy method to generate Proxy class, and java language does not support multiple inheritance because of single inheritance, how can I identify which type of class or method I want to Proxy? Interface. java supports multiple inheritance of interfaces. How many are ok~

Well, the above unifies the use mode and implementation principle of dynamic agent, and answers several questions that are easy to be confused. Let's briefly talk about some typical applications of dynamic agent in the real java framework family

Application of dynamic agent

spring aop: this can be said to be the most typical application in the spring framework. Through the dynamic proxy, the proxy class is generated at run time to complete the enhancement and function addition of the proxy class

Implementation of RPC framework: remote procedure call, RPC makes calling remote method the same as calling local method, what's the matter? The service side releases the interface api of the service, the caller gets the interface api, and generates a proxy class through dynamic proxy. The invoke method of the proxy class's processing class can connect with the remote server through websocket to call the corresponding remote interface. In this way, when we use the proxy to call the corresponding method, it is like calling the local method

In mybatis framework: mapper.xml Write sql statement in, mapper.java The interface writes the corresponding method signature; we call mapper.java The method in can execute the corresponding sql statement. Have you ever thought about why? The framework uses dynamic agents to create a mapper.java It's ok to execute sql in the agent object's processing class invoke

summary

The agent is divided into static agent and dynamic agent. There are two ways to realize the dynamic agent: JDK Proxy and CGLib. The core reflection mechanism of the dynamic agent is to obtain the bytecode of the proxied class and the bytecode of the processing class by reflection at runtime. The generation of the dynamic agent class is achieved by recombining the bytecode.

It's not easy to be original. If there's harvest, I'm most motivated by the support of attention, praise and comment~

If you have any questions about the blog, please comment. Thank you

Reference: JDK source code, https://www.jianshu.com/p/861223789d53

Finally, for more technical blogs, please pay attention to:

Tags: Java JDK jvm SQL

Posted on Thu, 21 May 2020 11:59:19 -0400 by Dollar