虽然使用了动态代理,但是对其原理还不是十分了解。这段时间剖析了下JDK动态代理的源码,算是搞懂了JDK代理类的生成逻辑。下面,先来回顾下代理类的使用:
//接口对象public interface LogService { public void writeLog();}//接口实现类public class LogServiceImpl implements LogService { @Override public void writeLog() { // TODO Auto-generated method stub System.out.PRintln("-------------记录日志-----------"); }}//处理类public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object t) { // TODO Auto-generated constructor stub this.target=t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在目标对象的方法执行之前简单的打印一下 System.out.println("------------------before------------------"); // 执行目标对象的方法 Object result = method.invoke(target, args); // 在目标对象的方法执行之后简单的打印一下 System.out.println("-------------------after------------------"); return result; } public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); }}//测试类public class Test { public static void main(String[] args) { // TODO Auto-generated method stub LogService logService=new LogServiceImpl(); MyInvocationHandler myInvocationHandler=new MyInvocationHandler(logService); LogService logServiceProxy=(LogService) myInvocationHandler.getProxy(); logServiceProxy.writeLog(); }}执行结果:------------------before-------------------------------记录日志------------------------------after------------------2、源码分析
用起来是比较简单,但是如果能知道其背后的原理就再好不过了。重点在类MyInvocationHandler中的getProxy()方法,我们将目光放在下面这一行代码:Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(), this); Debug进入这段代码,可以得到如下片段():public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyaccess(Reflection.getCallerClass(), loader, interfaces); } /* * 重点在这句话,这句话是生成代理类关键的地方,我们下面来看 */ Class<?> cl = getProxyClass0(loader, interfaces); try { /* 这里大致解释一下,在Proxy类的开头定义了一个静态成员 private final static Class[] constructorParams ={ InvocationHandler.class }; 因此,这个constructorParams指的就是class数组. */ //这句就是从代理类中得到以InvocationHandler为参数的构造函数 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { //将构造函数和操作类进行封装,生成代理对象返回,原理是使用cons.newInstance(new Object[] {h} ); return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }接下来进入getProxyClass0方法,详细看看这块代码是如何实现的,这块代码比较长,分为几个小块进行解释
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {接下来进行接口的一些验证,大致可分为以下几步:
保证接口的数量在65535范围内 以interface的长度为准,建立一个名为interfaceNames字符数组,这个数组是用来作为代理类缓存的key定义一个检测是否存在重复接口的Set<Class<?>> interfaceSet 初始化interfaceNames数组,里面最终将填满接口的名称if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } Class<?> proxyClass = null; String[] interfaceNames = new String[interfaces.length]; Set<Class<?>> interfaceSet = new HashSet<>(); //初始化interfaceNames数组的代码略去,非核心代码,最终interfaceNames中讲填满接口名称接下来这段逻辑可以这么理解,以interfaceNames做key,将代理类缓存起来这样,我们的代码中第二次调用Proxy.newProxyInstance()方法时,就不需要再去动态生成代理类,而是直接去缓存中获取,可大大提高性能。
这段代码我就觉得sun写的相当棒,在此详细说明一下。以后如果遇到只需动态加载一次类的代码,可参照sun的这段逻辑:
先定义两个全局成员变量,注意着两个成员变量是在Proxy类中,而不是在getProxyClass0()方法中:
//这是一个缓存map,好好思考下为啥用ClassLoader做key哦,这个写法给我很大的启发。其中这个map的value又是一个map(称为值map)。而这个值map的key为List,就是我们刚提到的interfacenames,而Object,就是我们的缓存对象。通过下面代码的说明,大家很容易就能明白。 private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache = new WeakHashMap<>(); //标志位,标记动态代理类是不是正在生成。 private static Object pendingGenerationMarker = new Object();!!下面是这段缓存的主要逻辑,大家仔细看哦:
//将interfacename转为list,作为loaderCache的key List key = Arrays.asList(interfaceNames); /* * 这才是真正缓存我们代理类的map */ Map<List, Object> cache; synchronized (loaderToCache) { //这段逻辑很清晰,就是先从缓存中取,取不到就自己new一个塞进去 cache = loaderToCache.get(loader); if (cache == null) { cache = new HashMap<>(); loaderToCache.put(loader, cache); } } /* *假设程序中有以下两行代码 *LogService logServiceProxy1=(LogService) myInvocationHandler.getProxy(); (a) *LogService logServiceProxy2=(LogService) myInvocationHandler.getProxy(); (b) *OK,这段是核心,仔细看了哦,针对(a)和(b)在下面代码中详细说明 */ synchronized (cache) { do { Object value = cache.get(key); if (value instanceof Reference) { //代码(a)顺利执行,那么代码(b)就可以顺利取出proxyclass proxyClass = (Class<?>) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { //代码(a)不顺利,在动态生成代理类中耗时比较长,那么代码(b)执行到此处,将执行下方的wait方法,等在此处。 //代码(a)中的动态代理类顺利生成后,代码(b)因为一直执行do while语句,所以一定会取出proxyClass,从而返回。 try { cache.wait(); } catch (InterruptedException e) { } continue; } else { /*代码(a)运行的时候,由于缓存没东西,直接塞入正在生成代理类的标志位, *代码(b)就靠识别这个对象的内存地址,判断生成代理类的情况 */ cache.put(key, pendingGenerationMarker); break; } } while (true); }紧接着是一段生成包名的逻辑和生成代理类的逻辑。备注:由于生成代理类的逻辑代码比较复杂,将放在《JDK动态代理原理剖析(二)》
try { String proxyPkg = null; // package to define proxy class in //省去生成proxyPkg的逻辑,不是重点,知道会生成这样一个包名即可 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * ProxyGenerator.generateProxyClass()这个方法将在《JDK动态代理原理剖析(二)》中详细介绍,此方法比较复杂, * 这里只需知道,会生成一个代理类的字节流即可 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { /* * 将字节流生成类defineClass0是native方法,就不做介绍,只需明白是利用字节流生成代理类即可。 */ proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } proxyClasses.put(proxyClass, null); } finally { /* *这段代码很好理解,避免死锁 */ synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference<Class<?>>(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass;到这里,动态代理类如何生成的,逻辑一目了然了。最后,做出如下总结:
3、总结
JDK实现动态代理的过程如下:
克隆传入的接口Class数组查找或者生成实现了接口数组中所有接口的动态代理类的Class利用动态代理类的Class获取Constructor对象Constructor对象利用我们传入的InvocationHandler实现类对象作为输入参数,生成动态代理类的对象
新闻热点
疑难解答