Structural model -- dynamic agent in JDK mode of agent model

preface

In the last blog, I focused on the concept, structure and cases of agent model.

Link address: Structural model -- static agent of agent model

Structure of agent mode

In the agent mode, there are three roles:

  • Abstract topic class (Subject):

    Define specifications!
    Business methods implemented by real topics and proxy objects are declared through interfaces or abstract classes.

  • Real subject:

    The specific business in the abstract topic is the real object represented by the proxy object, that is, the object that needs to be referenced in the end.

  • Proxy class:

    It provides the same interface as the real topic, which contains references to the real topic. It can access, control or extend the functions of real topics.

JDK dynamic agent

Agent mode is generally divided into static agent and dynamic agent. The fundamental difference is that the proxy class is generated at compile time and during program execution.

Dynamic agents generally include JDK agents and CGLib agents. Next, the differences between the two methods of generating dynamic agents are described in detail.

Java provides a dynamic proxy class java.lang.reflect.Proxy, which provides a newproxyinstance (classloader, loader, class <? > [] interfaces, invocationhandler h) method to obtain the proxy object.

The main difference between dynamic proxy and static proxy lies in the generation method of proxy class objects!
Static agent is to write code, which is generated during program compilation according to the written code!
Dynamic Proxy is automatically generated according to the Proxy tool class and the given method!

Next, the code case is still used to explain the use.

1. Define abstract topic classes

Abstract topic class is actually a constraint (or specification).

package proxy.jdk;

/**
 * Abstract topic class: definition specification;
 * Subclasses include real topic classes and proxy objects
 */
public interface SellPhone {
    // Defining constraints, proxy objects and specific subject objects all have operations
    void sell();
}

2. Define specific topic implementation classes

This is still based on Huawei manufacturers as an example.

package proxy.jdk;

/**
 * Specific category: Huawei mobile phone manufacturer
 */
public class HWContractor implements SellPhone {
    @Override
    public void sell() {
        System.out.println("Huawei manufacturers sell mobile phones");
    }
}

3. Write a proxy class factory to generate dynamic proxy objects

As mentioned earlier in this section, the JDK implements dynamic proxy by adopting the newProxyInstance method in the dynamic proxy class java.lang.reflect.Proxy to obtain the proxy object.

The method has three parameter information.

The meanings of these three parameters are as follows:

namemeaning
ClassLoaderClass loader, used to load the proxy class. The class loader can be obtained through the target object (specific topic implementation class).
Both the proxy class and the concrete topic implementation class are subclasses of the abstract topic class
Class<?>[]The bytecode object of the interface implemented by the proxy class.
InvocationHandlerThe call handler for the proxy object.

The invoke method defined in the InvocationHandler interface in newProxyInstance also has three parameters:

The meanings of the parameters are as follows:

namemeaning
proxyProxy object, which is the same object as the object returned by Proxy.newProxyInstance.
methodMethod object that encapsulates the method in the interface.
argsThe actual parameters of the calling method.

According to the above instructions, write the dynamic agent factory implementation class:

package proxy.jdk;

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

/**
 * Gets the factory class of the proxy object
 */
public class ProxyFactory {

    // Declare target object
    private HWContractor hwContractor = new HWContractor();

    // Provides a method to get a proxy object
    public SellPhone getProxyObj(){
        // Get Proxy object using Proxy

        /**
         * ClassLoader loader: Class loader for how to load proxy classes. The class loader can be obtained from the target object
         * Class<?>[] interfaces: Bytecode object of the interface implemented by the proxy class
         * InvocationHandler h: Call handler for proxy object
         */
        SellPhone proxyInstance = (SellPhone) Proxy.newProxyInstance(
                hwContractor.getClass().getClassLoader(),
                hwContractor.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * @param proxy Proxy object, which is the same object as proxyInstance
                     * @param method Method object that encapsulates the method in the interface
                     * @param args Actual parameters of the calling method
                     * @return Method
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // enhance
                        System.out.println("Commission fees earned by consignment points");
                        // Method of executing target object
                        Object obj = method.invoke(hwContractor, args);
                        return obj;
                    }
                });
        return proxyInstance;
    }
}

4. Write consumer

package proxy.jdk;

public class Client {
    public static void main(String[] args) {
        // 1. Create agent class factory
        ProxyFactory proxyFactory = new ProxyFactory();
        // 2. Create proxy objects through the methods in the custom proxy factory
        SellPhone proxyObj = proxyFactory.getProxyObj();
        // 3. Methods of using proxy objects
        proxyObj.sell();
    }
}

Dynamic agent source code analysis of JDK

In dynamic proxy, use Proxy.newProxyInstance to create a subclass of SellPhone interface, which can be seen from the structure of proxy mode:

1. The specific topic class is a subclass of SellPhone.
2. The proxy class is a subclass of SellPhone.

Then, Proxy.newProxyInstance is a way to automatically generate proxy classes.

You can see from the source code:

The logic here indicates that the proxy class is automatically generated.

Class<?> cl = getProxyClass0(loader, intfs);

Continue to enter it or run debug. The actual operation of generating agent class is as follows:

How does the specific logic operate when obtaining from a cache? Continue to look down:



Running according to the debug mode, it is found that subKeyFactory.apply(key, parameter) does not execute the default apply mode, but the apply method in the static internal class ProxyClassFactory.

It can be seen from the logic of this method:

Finally, take defineClass0 to generate a proxy class object information. This method is a native modification. You can save the generated information to the text for viewing.

What does ProxyGenerator.generateProxyClass do

Write code to save the array information to text.

import java.io.IOException;
import sun.misc.ProxyGenerator;
import java.nio.file.Files;
import java.io.File;
import java.nio.file.Path;
import java.lang.reflect.Modifier;

public class TestFile {
    public static void main(String[] args)throws IOException {

        // Copy the data information debugged in java.lang.reflect.proxy.proxyclassfactory.apply in the source code
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
        String proxyName = "proxy0";
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, new Class[] {SellPhone.class}, accessFlags);

        // Save to text as a stream
        File file = new File(System.getProperty("user.dir") + "/src/proxy/jdk/proxy0.class");
        //The existence of the parent directory must be guaranteed
        if(!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();//Create parent directory if it does not exist
        }
        Path path = file.toPath();
        System.out.println(path);
        Files.write(path, proxyClassFile);
    }
}

After running, you can see the specific file information in the corresponding package:

Extract the code related to the proxy class, as follows:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.jdk.SellPhone;

public final class proxy0 extends Proxy implements SellPhone {
	private static Method m3;
	static{
		m3 = Class.forName("proxy.jdk.SellPhone").getMethod("sell");
	}
	public final void sell() throws  {
		// Call the invoke method in the InvocationHandler of the parent class
		// The passed parameter is null
	    super.h.invoke(this, m3, (Object[])null);
	}
}

It can be seen that the real logic is very clear

The proxy mode class is also a subclass of SellPhone. As a subclass, it implements the parent class interface, and its subclass must override the abstract method,

Therefore, in the above code, sell() is the specific implementation of the proxy class. The logic is super.h.invoke, which calls the invoke method of class h in the parent class.

h.invoke in the parent class is the following in program code writing:

Finally, the common method of obtaining the reflected class is adopted to execute its specific logic.

Reference materials

Basic usage of reflection - point 6

Test code download

Download address of this blog

Keywords: Java Algorithm

Added by devans on Wed, 24 Nov 2021 09:33:00 +0200