锁定老帖子 主题:JDK的动态代理是如何实现的?
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-04-14
只知道可以用JDK的动态代理,基于接口,或者是cglib方式字节码增强.目前对JDK的动态代理方式不是很理解,固拿出来探讨下,我知道这应该是个新手贴,但还是请大家给点意见.如果真正理解了动态代理,那么对基于Spring AOP出现的各种现象就比较容易理解了. 先贴上一段代码. package com.jamesby.proxy; public interface CarCompany { public void makeCar(); public void abc(); } package com.jamesby.proxy; public class CarCompanyImpl implements CarCompany { public void makeCar() { System.out.println("Company A make a car!"); abc(); } public void abc() { System.out.println("abc is invoked!"); } } package com.jamesby.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class CarCompanyHandler implements InvocationHandler { CarCompany com; public CarCompanyHandler(CarCompany com) { this.com = com; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("time1:" + System.currentTimeMillis()); method.invoke(this.com, new Class[] {}); System.out.println("time2:" + System.currentTimeMillis()); return null; } } package com.jamesby.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] arg) { CarCompanyImpl a = new CarCompanyImpl(); CarCompanyHandler handler = new CarCompanyHandler(a); // 产生一个新的代理类 CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class .getClassLoader(), new Class[] { CarCompany.class }, handler); com.makeCar(); } } 从以上代码可以看出在调用com.makeCar()的时候动态代理会生效,而在makeCar()方法中调用abc()的时候并不会进行动态代理. 猜测到底JDK是如何实现动态代理的? 1.基于JDK的反射机制,通过JDK调用handler的method.invoke(this.com, new Class[] {});实现对最终方法的调用,前后各有代码也有点Decorator的味道! 2.也是我比较模糊的一点 CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class .getClassLoader(), new Class[] { CarCompany.class }, handler); com.makeCar(); 也就是动态代理JDK到底是如何实现的? JDK的动态代理并没有生成具体的类,她只是在运行中起动态调用目标类的作用,那么代码 CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class .getClassLoader(), new Class[] { CarCompany.class }, handler); 究竟做了什么,是将CarCompany注册到JDK的某个地方,当调用com.makeCar()的时候JDK到注册的地方去寻找,发现有注册就进行动态代理? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-04-14
先贴段JDK的代码,大家帮助分析下:
package java.lang.reflect; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import sun.misc.ProxyGenerator; public class Proxy implements java.io.Serializable { private static final long serialVersionUID = -2222568056686623797L; private final static String proxyClassNamePrefix = "$Proxy"; private final static Class[] constructorParams ={ InvocationHandler.class }; private static Map loaderToCache = new WeakHashMap(); private static Object pendingGenerationMarker = new Object(); private static long nextUniqueNumber = 0; private static Object nextUniqueNumberLock = new Object(); private static Map proxyClasses =Collections.synchronizedMap(new WeakHashMap()); protected InvocationHandler h; private Proxy() { } protected Proxy(InvocationHandler h) { this.h = h; } //关键是这个方法,正在看. public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } Class proxyClass = null; String[] interfaceNames = new String[interfaces.length]; Set interfaceSet = new HashSet(); // for detecting duplicates for (int i = 0; i < interfaces.length; i++) { String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } Object key = Arrays.asList(interfaceNames); Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } } synchronized (cache) { do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { return proxyClass; } else if (value == pendingGenerationMarker) { try { cache.wait(); } catch (InterruptedException e) { } continue; } else { cache.put(key, pendingGenerationMarker); break; } } while (true); } try { String proxyPkg = null; // package to define proxy class in for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].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, proxyPkg = ""; // use the unnamed package } { long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { 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(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; } //这个就是我的例子中调用的方法. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } Class cl = getProxyClass(loader, interfaces); try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } } public static boolean isProxyClass(Class<?> cl) { if (cl == null) { throw new NullPointerException(); } return proxyClasses.containsKey(cl); } public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { if (!isProxyClass(proxy.getClass())) { throw new IllegalArgumentException("not a proxy instance"); } Proxy p = (Proxy) proxy; return p.h; } private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len); } |
|
返回顶楼 | |
发表时间:2007-04-14
Class cl = getProxyClass(loader, interfaces);
生成实现接口的类,并自动设置一个ContractorMethodName(InvocationHandler h)的构造函数? 如果这样就还有一个问题也是最关键的问题搞不懂. 就是InvocationHandler 的invoke是如何被调用的?是对代理接口的每个声明的方法生成类似如下代码? makeCar() { handler.invoke(......); } |
|
返回顶楼 | |
发表时间:2007-04-14
正解!
http://www.blogjava.net/AndersLin/archive/2006/06/11/51997.html |
|
返回顶楼 | |
发表时间:2007-04-14
yimlin 写道 正解!
http://www.blogjava.net/AndersLin/archive/2006/06/11/51997.html 多谢楼上,看来我的猜测是对的! 这样来说,对于Spring AOP来说,比如事务拦截的时候,只拦截某些方法,实际上Spring 应该是在类似如下代码做文章! public class CarCompanyHandler implements InvocationHandler { CarCompany com; public CarCompanyHandler(CarCompany com) { this.com = com; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //此处做文章,如果是需要事务拦截的,则此处添加事务代,否则什么也不做. method.invoke(this.com, new Class[] {}); return null; } } 如果以上说法成立,那么实际上对于已经被动态代理的接口来讲,无论方法是否匹配,都是通过java的反射机制来对目标类实现调用的. 也就是说一个interface有10个方法声明,但是我只对其中一个方法配置事务属性,结果是对该接口的所有方法调用均是通过发射机制完成的,当然对其中配置事务属性的方法进行事务拦截. PS:楼上给的blog地址是你自己的blog吗? |
|
返回顶楼 | |
发表时间:2007-04-14
这样就可以很好的理解为什么service调用service本身的方法AOP起不到拦截作用了.
因为Spring 将Service 注入到Controller 或者Action的时候注入的是动态代理类,而service调用service自己的方法这时候的对象句柄this实际上是自己实现ServiceImpl类.因此AOP拦截不到. |
|
返回顶楼 | |
发表时间:2007-04-14
还有一个问题,那就是如果为一个ServiceImpl配置多个拦截器的时候到底生成多少个动态代理类的问题!
一个动态代理类? 如果一个那多个拦截器如何按照次序拦截? |
|
返回顶楼 | |
发表时间:2007-04-14
应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。
|
|
返回顶楼 | |
发表时间:2007-04-14
janh 写道 应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。 我也是这样一来理解的,在看Spring AOP的代码!
|
|
返回顶楼 | |
发表时间:2007-04-15
看看jdk动态代理反编译后的<? extends Proxy>
如果有兴趣的话再看看 cglib(asm), javasist |
|
返回顶楼 | |