曾几何时本人对Spring AOP感到神秘莫测,无比膜拜。感叹它的拦截功能无所不能,感叹它一统天下的 雄心壮志!上周闲来无事,索性也来拦截一把!开始做起了代理商。。。
说到AOP不得不说到代理模式,说到代理模式又不禁联想到Java动态代理。正如你说猜想的,解决了Java动态代理机制Sping AOP也不再是神话!现在开始探索Java动态代理机制先。。。
首先请看java.lang.reflect下有个proxy,不管三七二十几把它晒晒再说。(详细代码参见:java.lang.reflect.Proxy.java)顾名思义,这就是代理的真正元凶! Proxy元凶有如下特征:
-String proxyClassNamePrefix = "$Proxy"; // 代理类名前缀
-Class[] constructorParams = {InvocationHandler.class}; // 代理类构造函数参数列表
-Map loaderToCache = new WeakHashMap(); // 缓存代理类加载器
-Object pendingGenerationMarker = new Object(); // 标记代理实例是否被创建
-Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 缓存代理实例
protected InvocationHandler h; // 引用调用处理程序对象
Proxy元凶有如下罪行:
private Proxy(){} // 构造器私有化 protected Proxy(InvocationHandler h){this.h = h}
注:红色标注表明它是团伙作案的。它一人是不能完成的,至少二人以上作案。
+ isProxyClass(Class<?> clazz); // 判定是否是代理类
+ getInvocationHandler(Object proxy);// 获取指定代理实例的调用处理程序。
+ newProxyInstance(ClassLoader loader , Class<?>... interfaces); // 获取代理实例
+ getProxyClass(ClassLoader loader , Class<?> ... interfaces) // 获取代理类
如果注意该类doc信息,你不难发现创建proxy有两种方式:
Method 1:
InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass.getConstructor(new Class[] {InvocationHandler.class }).newInstance(new Object[] { handler });
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] {Foo.class },handler);
很明显,大家都喜欢简单明了的第二种方式。其实第二种方式就是对一种方式的封装。看来sun为我们考虑的还是蛮周到的嘛~大家看newProxyInstance方法如此简单,不就是获取到代理类,然后根据反射机制生成代理实例而已。没错,重头戏还是如何获取代理类,如何将委托的信息交给代理类呢?我们就需要分析一下getProxyClass~进入该方法刚开始也就是做一些安全之类的检测(被代理类是否是接口类型,包名检查。。。),当你看到这里就要注意了!
String proxyName = proxyPkg + proxyClassNamePrefix + num; // 生成类名 eg com.proxy.demo$Proxy1 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); //生成代理接口字节码 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); // 将生成的代理接口字节码注入到类加载器中
由于 denfineClass0属于本地代码,如名字一样也就是生成类信息。你可以理解为将生成的代理接口字节码注入到类加载器中,动态生成代理类。那么关键问题就在ProxyGenerator.generateProxy了,可是sun并未提供源码,不过也没有关系,我们可以模拟实现嘛!预知后事如何,参见模拟实现Java动态代理机制 !