Idle nothing, lala source code

How to eat it

Just look at the code.

package com.test.demo.proxy;

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

/** * Inside each proxy object is an instance of a class that implements the InvocationHandler interface ** InvocationHandler is, as the name implies, the handler class for the proxy object's method invocation (which invokes its invoke method) **@author lizhecao 2018/4/19
 * @version1.0 * /
public class InvocationHandlerImpl implements InvocationHandler {
  // Target object
  private Object target;

  public InvocationHandlerImpl(Object target) {
    this.target = target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // The proxy method simply logs, which is a simple implementation of Spring AOP
    System.out.println("start");

	// Call the actual method of the target object
    Object result = method.invoke(target, args);

    System.out.println("end");

    return  result;
  }

  /** * Generate Proxy objects with proxy.newproxyinstance (important method) */
  public Object getProxy(a) {
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
        target.getClass().getInterfaces(), this);
  }

  public static void main(String[] args) {
    // Instantiate the target object
    User user = new UserImpl();

    // Instantiate InvocationHandler with the target object
    InvocationHandlerImpl invocationHandler = new InvocationHandlerImpl(user);

    // Generate a proxy object
    User proxyUser = (User) invocationHandler.getProxy();

    // Invoke the invoke method of the invocationHandlerproxyUser.sayHello(); }}Copy the code
package com.test.demo.proxy;

/** * Target class interface, JDK dynamic proxy class must implement some interface **@author lizhecao 2018/4/19
 * @version1.0 * /
public interface User {
  void sayHello(a);
}
Copy the code
package com.test.demo.proxy;

/** * The specific class of the target object **@author lizhecao 2018/4/19
 * @version1.0 * /
public class UserImpl implements User {
  @Override
  public void sayHello(a) {
    System.out.println("hello"); }}Copy the code

Running the main method in InvocationHandlerImpl gives us the result:

start
hello
end
Copy the code

Understand the principle

As you can see from the example above, the point of generating proxy objects is

Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
        target.getClass().getInterfaces(), this);
Copy the code

This method, we go into this method to see the JDK dynamic proxy implementation. To simplify, delete some things that do not pay attention to, have an interest in their own one by one

	/** * returns the proxy class instance of the specified interface and assigns the method call to the specified invocationHandler **@paramLoader The class loader used to define the proxy class@paramInterfaces the interfaces that the proxy class implements (it is also illustrated here that JDK dynamic proxy objects must implement interfaces) *@paramH The Invocation Handler * for assigning method calls@returnAn instance of the Invocation class */ that implements a specific interface with a specific Class loader define and includes a specific Invocation Hander
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
	    // Hanlder is not allowed to be empty.
        Objects.requireNonNull(h);
		// Prevent the interface from being modified
        finalClass<? >[] intfs = interfaces.clone();/* * find or produce the specified proxy Class (i.e., cache). * /Class<? > cl = getProxyClass0(loader, intfs);/* * Create a proxy class instance */ using our proxy class handler as a constructor parameter
        finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
        // Forcibly change constructor access to public
        if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run(a) {
                    cons.setAccessible(true);
                    return null; }}); }return cons.newInstance(new Object[]{h});
    }
Copy the code

How does getProxyClass0(loader, INTFS) produce proxy classes

    private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) {// The number of interfaces cannot exceed 65535
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        
        // Get it from WeakCache's cache, if not create it
        return proxyClassCache.get(loader, interfaces);
    }
Copy the code

WeakCache is used to cache the instance of the proxy class

    /** * a cache of proxy classes */
    private static finalWeakCache<ClassLoader, Class<? >[], Class<? >> proxyClassCache =new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
Copy the code

Here is a simple talk about WeakCache, or friends who have not played may not go down. WeakCache<K, P, V> caches key-value pairs such as <key, sub-key> -> value. Key and value are weakly referenced, while sub-key is strongly referenced (of course, this is not important). K, P, and V represent the types of key, parameter, and value respectively. One wonders, what about the sub key? What are parameters for? Let’s focus on how it’s constructed

    / * * *@paramSubKeyFactory A function such as (key, parameter) -> sub-key *@paramValueFactory A function like (key, parameter) -> value */
    public WeakCache(BiFunction
       
         subKeyFactory, BiFunction
        
          valueFactory)
        ,>
       ,> {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
Copy the code

If we pass in two function classes, we can generate sub-key and value from key and parameter, respectively

// The first time the proxy class is fetched, the subkey and value of the cache are generated by the loader and interfaces respectively, and the value is returned
return proxyClassCache.get(loader, interfaces);
Copy the code

Now that the value is generated by valueFactory and the parameters are loader and interface, let’s look at the instantiation method of proxyClassCache: New WeakCache<>(new KeyFactory(), new ProxyClassFactory())) Interface) -> proxyClass -> proxyClass

   /** * uses the given ClassLoader and interface array to generate, define, and return a function factory for the proxy class */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader.Class<? > [].Class<? >>{
        // The prefix of all proxy class names (sometimes debug will see this name frequently lol)
        private static final String proxyClassNamePrefix = "$Proxy";

        // The integer followed by the proxy class name (an incremented sequence)
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

		// Where the real action is
        @Override
        publicClass<? > apply(ClassLoader loader, Class<? >[] interfaces) { Map<Class<? >, Boolean> interfaceSet =new IdentityHashMap<>(interfaces.length);
            for(Class<? > intf : interfaces) {/* * Verify that the Class name is the same as the one we passed in */Class<? > interfaceClass =null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                // If not, throw an exception.
                if(interfaceClass ! = intf) {throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /* * verify whether the interface is an interface */
                if(! interfaceClass.isInterface()) {throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /* * Verify that */ is repeated
                if(interfaceSet.put(interfaceClass, Boolean.TRUE) ! =null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            * If there is a non-public interface, the proxy class should be placed in the same directory as it * and the non-public interface must be placed in the same directory */
            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)) {throw new IllegalArgumentException(
                            "non-public interfaces from different packages"); }}}// If the interface is public, then com.sun.proxy package
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /* * The unique code */ of a proxy class generated by atomic numeric increment
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            ProxyGenerator generates an array of bytes from the proxy class file */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
	            // Use native methods to generate Class objects
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw newIllegalArgumentException(e.toString()); }}}Copy the code

ProxyGenerator is a method that comes with the Sun.misc package and is not open source, so we can simply implement it

  public static byte[] generateProxyClass(finalString var0, Class<? >[] var1,int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    // Generate an array of bytes for the class file
    final byte[] var4 = var3.generateClassFile();
    // If saveGeneratedFiles is true, the. Class file is written to disk
    if (saveGeneratedFiles) {
      // omit the specific method of saving the file
    }

    return var4;
  }
Copy the code

Take a look at this condition for saving the file

  private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
Copy the code

That is to say as long as we set up the “sun. Misc. ProxyGenerator. SaveGeneratedFiles” system value is true, it can save the file say a few words to see the JDK dynamic proxy automatically generated this class file to us long what kind, use the following code to test

package com.test.demo.proxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

/** * Write proxy class files to disk **@author lizhecao 2018/4/20
 * @version1.0 * /
public class ProxyClassGen {
  public static void main(String[] args) throws IOException {

// // The first method is to write the generated byte array directly to a file
// byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy33", UserImpl.class.getInterfaces());
//
// try(FileOutputStream outputStream = new FileOutputStream(
// "/Users/lizhecao/java/demo/src/main/java/com/test/demo/proxy/$Proxy33.class")) {
// outputStream.write(bytes);
/ /}
    // The second way, set the system property value, have JDK automatic write
    System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
    ProxyGenerator.generateProxyClass("$Proxy34", UserImpl.class.getInterfaces()); }}Copy the code

After you run it, you can see that $proxy34.class is generated in the root directory of the project. Open it directly with the IDE and you can see the following

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.test.demo.proxy.User;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy34 extends Proxy implements User {
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  public $Proxy34(InvocationHandler var1) throws  {
    super(var1);
  }

  // In addition to the interface methods, the equals, hashCode and toString methods of Object are implemented
  public final boolean equals(Object var1) throws  {
    try {
      return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
    } catch (RuntimeException | Error var3) {
      throw var3;
    } catch (Throwable var4) {
      throw newUndeclaredThrowableException(var4); }}// implements the sayHello method of our User interface
  public final void sayHello(a) throws  {
    try {
      // Inside is the invokeHandler instance invoke method
      // M3 is the reflection sayHello Method of the User interface
      // Class.forName("com.test.demo.proxy.User").getMethod("sayHello");
      super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw newUndeclaredThrowableException(var3); }}public final String toString(a) throws  {
    try {
      return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw newUndeclaredThrowableException(var3); }}public final int hashCode(a) throws  {
    try {
      return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw newUndeclaredThrowableException(var3); }}static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
      m3 = Class.forName("com.test.demo.proxy.User").getMethod("sayHello");
      m2 = Class.forName("java.lang.Object").getMethod("toString");
      m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
      throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
      throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code

At this point, the JDK dynamic proxy is clear as day

Thanks to the following bloggers for sharing: JDK dynamic proxy implementation principles

This article is published by OpenWrite, a blogging tool platform