这篇文章主要介绍了Java JDK动态代理的基本原理详细介绍的相关资料,这里对动态代理进行了详解并附简单实例代码,需要的朋友可以参考下
JDK动态代理详解
本文主要介绍JDK动态代理的基本原理,让大家更深刻的理解JDK Proxy,知其然知其所以然。明白JDK动态代理真正的原理及其生成的过程,我们以后写JDK Proxy可以不用去查demo,就可以徒手写个完美的Proxy。下面首先来个简单的Demo,后续的分析过程都依赖这个Demo去介绍,例子采用JDK1.8运行。
JDK Proxy HelloWorld
package com.yao.proxy; /** * Created by robin */ public interface Helloworld { void sayHello(); }
package com.yao.proxy; import com.yao.HelloWorld; /** * Created by robin */ public class HelloworldImpl implements HelloWorld { public void sayHello() { System.out.print("hello world"); } }
package com.yao.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Created by robin */ public class MyInvocationHandler implements InvocationHandler{ private Object target; public MyInvocationHandler(Object target) { this.target=target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("method :"+ method.getName()+" is invoked!"); return method.invoke(target,args); } }
package com.yao.proxy; import com.yao.HelloWorld; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; /** * Created by robin */ public class JDKProxyTest { public static void main(String[]args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //这里有两种写法,我们采用略微复杂的一种写法,这样更有助于大家理解。 Class proxyClass= Proxy.getProxyClass(JDKProxyTest.class.getClassLoader(),HelloWorld.class); final Constructor cons = proxyClass.getConstructor(InvocationHandler.class); final InvocationHandler ih = new MyInvocationHandler(new HelloworldImpl()); HelloWorld helloWorld= (HelloWorld)cons.newInstance(ih); helloWorld.sayHello(); //下面是更简单的一种写法,本质上和上面是一样的 /* HelloWorld helloWorld=(HelloWorld)Proxy. newProxyInstance(JDKProxyTest.class.getClassLoader(), new Class[]{HelloWorld.class}, new MyInvocationHandler(new HelloworldImpl())); helloWorld.sayHello(); */ } }
运行上面的代码,这样一个简单的JDK Proxy就实现了。
代理生成过程
我们之所以天天叫JDK动态代理,是因为这个代理class是由JDK在运行时动态帮我们生成。在解释代理生成过程前,我们先把-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 这个参数加入到JVM 启动参数中,它的作用是帮我们把JDK动态生成的proxy class 的字节码保存到硬盘中,帮助我们查看具体生成proxy的内容。我用的Intellij IDEA ,代理class生成后直接放在项目的根目录下的,以具体的包名为目录结构。
代理类生成的过程主要包括两部分:
- 代理类字节码生成
- 把字节码通过传入的类加载器加载到虚拟机中
Proxy类的getProxyClass方法入口:需要传入类加载器和interface
然后调用getProxyClass0方法,里面的注解解释很清楚,如果实现当前接口的代理类存在,直接从缓存中返回,如果不存在,则通过ProxyClassFactory来创建。这里可以明显看到有对interface接口数量的限制,不能超过65535。其中proxyClassCache具体初始化信息如下:
proxyClassCache = new WeakCache(new KeyFactory(), new ProxyClassFactory());
其中创建代理类的具体逻辑是通过ProxyClassFactory的apply方法来创建的。
ProxyClassFactory里的逻辑包括了包名的创建逻辑,调用ProxyGenerator. generateProxyClass生成代理类,把代理类字节码加载到JVM。
1.包名生成逻辑默认是com.sun.proxy,如果被代理类是 non-public proxy interface ,则用和被代理类接口一样的包名,类名默认是$Proxy 加上一个自增的整数值。
2.包名类名准备好后,就是通过ProxyGenerator. generateProxyClass根据具体传入的接口创建代理字节码,-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 这个参数就是在该方法起到作用,如果为true则保存字节码到磁盘。代理类中,所有的代理方法逻辑都一样都是调用invocationHander的invoke方法,这个我们可以看后面具体代理反编译结果。
3.把字节码通过传入的类加载器加载到JVM中: defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);。
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class[], Class> { // prefix for all proxy class names private 来源gaodai$ma#com搞$代*码网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 (interfaceSe以上就是Java JDK动态代理的基本原理详细介绍的详细内容,更多请关注gaodaima搞代码网其它相关文章!