JDK动态代理

1.动态代理

代理模式一般用于增强类的方法而不直接修改原始类的代码,动态代理可以用于动态生成目标类的代理类来执行相应操作,目前动态代理主要有JDK动态代理以及CGLIB,经常会提起,也会看一下解释,但是并没有真正实践过,现在学习一下。

JDK动态代理

JDK动态代理,使用反射机制,一般用来代理接口类。

具体来说

1.要有目标接口

JDK动态代理

目标接口实现类

JDK动态代理

2.实现InvocationHandler,进行目标方法的增强

JDK动态代理

3.调用Proxy.newProxyInstance, 产生动态代理类,执行方法

第一种调用代理类的方式

JDK动态代理

首先获取代理类,获得代理类的构造函数,通过构造函数来生成动态代理对象,调用动态对象目标方法

第二种调用方式Proxy直接对以上操作进行了封装

JDK动态代理

此外,我们使用ProxyGenerator来生成动态代理类并保存class文件,以便进行反编译分析,项目中导入ProxyGenerator类,进行字节码的生成

JDK动态代理

2.动态代理类的分析:

反编译Proxy0.class文件如下所示:

import com.sun.proxy..Proxy0;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Proxy0 {
	private static Method m1;
	private static Method m6;
	private static Method m2;
	private static Method m7;
	private static Method m11;
	private static Method m13;
	private static Method m0;
	private static Method m8;
	private static Method m3;
	private static Method m12;
	private static Method m5;
	private static Method m10;
	private static Method m4;
	private static Method m9;

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

	public final InvocationHandler getInvocationHandler(Object var1) throws IllegalArgumentException {
		try {
			return (InvocationHandler) super.h.invoke(this, m6, new Object[]{var1});
		} catch (RuntimeException | Error var3) {
			throw var3;
		} catch (Throwable var4) {
			throw new UndeclaredThrowableException(var4);
		}
	}

	public final Class getProxyClass(ClassLoader var1, Class[] var2) throws IllegalArgumentException {
		try {
			return (Class) super.h.invoke(this, m7, new Object[]{var1, var2});
		} catch (RuntimeException | Error var4) {
			throw var4;
		} catch (Throwable var5) {
			throw new UndeclaredThrowableException(var5);
		}
	}

	public final Class getClass() throws  {
      try {
         return (Class)super.h.invoke(this, m11, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

	
	public final void sayHello() throws  {
      try {
         super.h.invoke(this, m3, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

	public final Object newProxyInstance(ClassLoader var1, Class[] var2, InvocationHandler var3)
			throws IllegalArgumentException {
		try {
			return (Object) super.h.invoke(this, m5, new Object[]{var1, var2, var3});
		} catch (RuntimeException | Error var5) {
			throw var5;
		} catch (Throwable var6) {
			throw new UndeclaredThrowableException(var6);
		}
	}

	

	public final boolean isProxyClass(Class var1) throws  {
      try {
         return (Boolean)super.h.invoke(this, m4, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

	public final void wait(long var1, int var3) throws InterruptedException {
		try {
			super.h.invoke(this, m9, new Object[]{var1, var3});
		} catch (RuntimeException | InterruptedException | Error var5) {
			throw var5;
		} catch (Throwable var6) {
			throw new UndeclaredThrowableException(var6);
		}
	}

	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
			m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler",
					Class.forName("java.lang.Object"));
			m2 = Class.forName("java.lang.Object").getMethod("toString");
			m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass",
					Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
			m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass");
			m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll");
			m0 = Class.forName("java.lang.Object").getMethod("hashCode");
			m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait");
			m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("sayHello");
			m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify");
			m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance",
					Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"),
					Class.forName("java.lang.reflect.InvocationHandler"));
			m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE);
			m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", Class.forName("java.lang.Class"));
			m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE, Integer.TYPE);
		} catch (NoSuchMethodException var2) {
			throw new NoSuchMethodError(var2.getMessage());
		} catch (ClassNotFoundException var3) {
			throw new NoClassDefFoundError(var3.getMessage());
		}
	}
}

动态代理类继承了Proxy类,实现了Proxy0接口,首先观察静态初始化代码块,主要方法均是来自Object,Proxy,ClassLoader等类的方法,其中m3方法获取到了要代理的目标接口,sayHello方法具体转发到了自定义的InvacationHandler方法中,来执行自定义的逻辑。

3.Proxy类分析

首先看一下newProxyInstance方法:

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

检查InvocationHandler非空,复制目标接口,进行安全检查,产生代理类,根据InvocationHandler,使用代理类的构造器来产生代理对象,返回代理对象。

其中重要的方法getProxyClass0方法:

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // 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
        return proxyClassCache.get(loader, interfaces);
    }

可知动态代理类,对多代理65535个接口,代理类从缓存中取出。

JDK动态代理

其中KeyFactory

JDK动态代理

具体代理类的产生由ProxyClassFactory产生:

 /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                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;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            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 (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

代理类的名字前缀为$Proxy,并且有一个原子类型的数字(多线程安全)来标识代理类的序号。

proxyPkg定义了代理类的生成位置,与接口同一路径下,如果没有接口,则使用路径ReflectUtil.PROXY_PACKAGE 即com.sun.proxy 

代理类的名字  String proxyName = proxyPkg + proxyClassNamePrefix + num;   包名+$Proxy+num;

调用ProxyGenerator.generateProxyClass来生成字节码文件

JDK动态代理

其中generateClassFile方法来产生字节码文件,具体涉及到class文件的结构,留着慢慢研究。

4.实现简单动态代理

使用JavaCompliler需要使用tools.jar,启动java的目录默认是%JAVA_HOME%/jre/bin/java.exe,这个目录的lib目录为%JAVA_HOME%/jre/lib,里面没有tools.jar,从%JAVA_HOME%/lib下面拷贝tools.jar到jre的lib目录下.

4.1定义InvocationHandler

JDK动态代理

4.2 定义InvocationHandler实现类,实现代码增强逻辑

JDK动态代理

4.3 定义类加载器

JDK动态代理

4.4 定义代理产生类

public class SimpleProxy {
	
	private static String ln = "\r\n";
	
	public static Object newProxyInstance(SimpleClassLoader loader, 
										Class<?> interfaces, 
										SimpleInvocationHandler h) 
												throws IllegalArgumentException{
		/**
         * 1.生成代理类的源代码
         * 2.将生成的源代码输出到磁盘,保存为.java文件
         * 3.编译源代码,并生成.java文件
         * 4.将class文件中的内容,动态加载到JVM中
         * 5.返回被代理后的代理对象
         */
		try {
            // 1.生成代理类的源代码
            String src = generateProxyClassResource(interfaces);
            // 2.将生成的源代码输出到磁盘,保存为.java文件
            String path = SimpleProxy.class.getResource("").getPath();
            System.out.println(path);
            File file = new File(path+"$Proxy0.java");
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.close();

            // 3.编译源代码,并生成.class文件
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
            
            StandardJavaFileManager manager = javaCompiler.getStandardFileManager(diagnostics, null, null);
            
            Iterable iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();

            // 4.将class文件中的内容,动态加载到JVM中
            Class proxyClass = loader.findClass("$Proxy0");

            // 5.返回被代理后的代理对象
            Constructor c = proxyClass.getConstructor(SimpleInvocationHandlerImpl.class);
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
	}
	
	private static String generateProxyClassResource(Class<?> interfaces){
		StringBuilder src = new StringBuilder();
        src.append("package proxytest;").append(ln)
                .append("import java.lang.reflect.Method;").append(ln)
                .append("public class $Proxy0 implements ").append(interfaces.getName()).append("{").append(ln)
                .append("private SimpleInvocationHandler h;").append(ln)
                .append("public $Proxy0(SimpleInvocationHandler h){").append(ln)
                .append("this.h=h;").append(ln)
                .append("}").append(ln);

        for(Method method:interfaces.getMethods()){
            src.append("public ").append(method.getReturnType()).append(" ").append(method.getName()).append("() {").append(ln)
                    .append("try {").append(ln)
                    .append("Method m = ").append(interfaces.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append(ln)
                    .append("this.h.invoke(this, m, new Object[]{});").append(ln)
                    .append("}catch (Throwable e){").append(ln)
                    .append("e.printStackTrace();").append(ln)
                    .append("}").append(ln)
                    .append("}").append(ln);
        }
        src.append("}");
        return src.toString();
	}
}

4.5调用实现代理功能

JDK动态代理

参考文献:

https://www.jianshu.com/p/3616c70cb37b

https://blog.csdn.net/zhangdefeng2008/article/details/79399898