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:
name | meaning |
---|---|
ClassLoader | Class 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. |
InvocationHandler | The 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:
name | meaning |
---|---|
proxy | Proxy object, which is the same object as the object returned by Proxy.newProxyInstance. |
method | Method object that encapsulates the method in the interface. |
args | The 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