Day6 Java Dynamic Proxy

Posted by mator on Tue, 04 Jan 2022 08:39:29 +0100

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

  1. Prepare a ClassLoader that can use the system
  2. Get Java lang.reflect. The defineClass0 (classloader, loader, string name, byte [] B, int off, int len) method of proxy.
  3. 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:

  1. 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}

  2. 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
    
  3. 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);
    
  4. 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.

  5. 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,

  6. 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).

  7. 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).

  8. This class rewrites Java. Net as a proxy toString, hashCode and equals methods of lang.Object class.

  9. 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:

  1. Calling its getSerialVersionUID method will return 0L.
  2. Calling its getFields method returns an array of zero length.
  3. 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.

Topics: Java