JAVA Dynamic Proxy
In fact, the main implementation is to enhance the method without modifying the original code. See the following code for the specific implementation.
0x01 API
Main methods
package java.lang.reflect; import java.lang.reflect.InvocationHandler; /** * Creator: yz * Date: 2020/1/15 */ public class Proxy implements java.io.Serializable { // Omit member variables and some class methods /** * Gets the dynamic proxy processing class object * * @param proxy Returns the proxy instance of the calling handler * @return Call handler for proxy instance * @throws IllegalArgumentException If the parameter is not a proxy instance */ public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { ... } /** * Create a dynamic proxy class instance * * @param loader Specifies the class loader for the dynamic proxy class * @param interfaces Specifies the interface array to be implemented by the class of the dynamic proxy class * @param h Dynamic proxy processing class * @return Returns the proxy class instance generated by the dynamic proxy * @throws IllegalArgumentException Incorrect parameter exception */ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... } /** * Create a dynamic proxy class * * @param loader Defines the class loader for the proxy class * @param interfaces List of interfaces to be implemented by proxy class * @return The proxy class defined with the specified class loader, which can implement the specified interface */ public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) { ... } /** * Detect whether a class is a dynamic proxy class * * @param cl Class to test * @return true if the class is a proxy class, otherwise false */ public static boolean isProxyClass(Class<?> cl) { return java.lang.reflect.Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl); } /** * Defines a class object to the specified class loader * * @param loader Class loader * @param name Class name * @param b Class bytecode * @param off Intercept start position * @param len Intercept length * @return JVM Class object created */ private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len); }
newProxyInstance: get a newly generated dynamic proxy class instance
InvocationHandler: get class
getProxyClass: create class
And the invocationHandler method
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
0x02 use Java lang.reflect. Proxy dynamically creates class objects
In fact, it also has a native method called defineClass0, which can directly and dynamically create classes. (say hello word directly to the jvm)
The general process is
- Prepare a ClassLoader that can use the system
- Get Java lang.reflect. The defineClass0 (classloader, loader, string name, byte [] B, int off, int len) method of proxy.
- Return to the invoke class of shemethod.
0x03 using JDK dynamic proxy to generate FileSystem dynamic proxy class instance
* Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), * new Class() { Foo.class }, * handler);
handler needs to implement InvocationHandler interface
To override his constructor and invoke methods
// Create an instance of the UnixFileSystem class FileSystem fileSystem = new UnixFileSystem(); // Create a dynamic proxy processing class InvocationHandler handler = new JDKInvocationHandler(fileSystem); // Generate a dynamic proxy class by specifying the class loader and the interface array implemented by the class Class proxyClass = Proxy.getProxyClass( FileSystem.class.getClassLoader(),// Specifies the class loader for the dynamic proxy class new Class[]{FileSystem.class}// Defines the interface implemented by the class generated by the dynamic proxy ); // Use reflection to get the Proxy class constructor and create a dynamic Proxy class instance FileSystem proxyInstance = (FileSystem) proxyClass.getConstructor( new Class[]{InvocationHandler.class}).newInstance(new Object[]{handler} ); //Proxy (invocationhandler H (Interface))
Class is a very useful class, which can be used to implement the reflection mechanism. Understand new Class[]{FileSystem.class} This is the way to write an interface. The Class object is used to represent the classes and interfaces in the currently running Java application.
package com.anbai.sec.proxy; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class JDKInvocationHandler implements InvocationHandler, Serializable { private final Object target; public JDKInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // In order not to affect the output of the test Demo, the toString method is ignored here if ("toString".equals(method.getName())) { return method.invoke(target, args); } System.out.println("About to call[" + target.getClass().getName() + "]Class[" + method.getName() + "]method..."); Object obj = method.invoke(target, args); System.out.println("Completed[" + target.getClass().getName() + "]Class[" + method.getName() + "]Method call..."); return obj; } }
In fact, I learned this from Java web before, which is to add a middleware. You call the proxy method, which filters the following, adds something, and then selects whether to trigger it.
The interface must be, so that the call can be reflected.
Post the words of big brother
The classes generated by dynamic proxy have the following technical details and features:
-
The dynamic proxy must be an interface class. An interface implementation class is generated dynamically to proxy the method call of the interface (reflection mechanism).
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Interface new Class[]{FileSystem.class}
-
The dynamic proxy class is generated by Java lang.reflect. Proxy. Proxyclassfactory creation.
proxyClassCache.get(loader, interfaces);
// If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory
-
ProxyClassFactory calls sun misc. The proxygenerator class generates the bytecode of this class and calls Java lang.reflect. Proxy. The defineclass0() method registers the class with the JVM.
*/ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
-
This class inherits from Java lang.reflect. Proxy and implements the interface class that needs to be proxy, because Java lang.reflect. The proxy class implements Java io. Serializable interface, so the proxied class supports serialization / deserialization.
-
This class implements the proxy interface class (the interface class in the example is com.anbai.sec.proxy.FileSystem), which will dynamically generate all methods of the interface class (FileSystem) through the ProxyGenerator,
-
Because this class implements the interface class of the agent, the current class is an instance of the interface class of the agent (proxyinstance instanceof file system is true), but not an instance of the implementation class of the agent interface class (proxyinstance instanceof unixfile system is false).
-
This class of methods contains all the methods of the interface class being proxied. The method execution results are obtained by calling the invoke method of the dynamic proxy processing class (InvocationHandler).
-
This class rewrites Java. Net as a proxy toString, hashCode and equals methods of lang.Object class.
-
If multiple dynamic proxy classes are generated through dynamic proxy, 0 in the newly generated class name will increase automatically, such as com sun. proxy.$ Proxy0/$Proxy1/$Proxy2.
0x04 dynamic proxy class instance serialization problem
Through the big brother's code, we know that the deserialized proxy class decompiled above is a subclass that responds when instantiated
reference resources: https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html#serial
That is, the class generated by the dynamic proxy will not serialize its member variables during serialization.
🔴 Pass the Class object of this Class to Java io. When using the static lookup method of ObjectStreamClass, the returned ObjectStreamClass instance will have the following attributes:
- Calling its getSerialVersionUID method will return 0L.
- Calling its getFields method returns an array of zero length.
- Calling its getField method will return null.
However, the proxy class is not affected. The H variable (InvocationHandler) will be serialized. This h stores the processing class instance of the dynamic proxy class and the implementation class instance of the interface class of the dynamic proxy.
resolveProxyClass is called when deserializing conversion, not resolveClass
🚗 It was extracted from the official documents here
If a proxy instance contains an invocation handler that is not assignable to java.io.Serializable, however, then a java.io.NotSerializableException will be thrown if such an instance is written to a java.io.ObjectOutputStream.
If an instance of a proxy class contains a handler that does not implement the serialization interface, the instance of the proxy class will report an error during serialization.
hat is not assignable to java.io.Serializable, however, then a java.io.NotSerializableException will be thrown if such an instance is written to a java.io.ObjectOutputStream.
If an instance of a proxy class contains a handler that does not implement the serialization interface, the instance of the proxy class will report an error during serialization.