最近呢,看到了一个开源的PRC FRAMEWORK,当然不是Dubbo。然后想去了解一下RPC到底是怎么去实现的。于是乎就了解了一番,发现其灵魂在于动态代理和反射。
What’s Dynamic Proxy ?
在讲什么是动态代理前先得明白什么是代理。在日常生活中的代理其实就是委托的意思。将事情交代给委托对象去做,这就是代理。程序中的代理是什么意思呢?这里更准确的解释应该是一种设计模式–代理模式(Proxy)
下面给出一个简单的关于代理的Java实现。
|
|
|
|
|
|
其中代理类中对主题接口进行了增强—在主题方法调用前后调用了before
和after
。Spring AOP中的思想正是如此。
这就是Java中的代理模式,只不过上面的代码实现是基于硬编码的,也就是所说的静态代理。那么区别于静态代理,那就一定有动态代理了。
通过以上的代码可以看出,静态代理将代码写死,是在编译阶段完成的具体代理类的绑定。但是动态代理不是这么做的,而是在程序运行时完成的这操作。下面使用动态代理方式实现。
|
|
由此可见,我们对主题对象所有的方法的调用都会变成对invoke
方法的调用,而我们可以在这个方法中添加统一的逻辑处理。
所以可以看出,动态代理的几个好处:
- 易于维护。相对于静态代理来说,只要在Proxy类中固定好处理逻辑而不用针对每个方法去编写代码了。控制了代码量,便于维护。
- 使AOP编程更加容易。在Spring的帮助下轻松添加、移除动态代理,且对源代码没有任何影响。
- 解耦。可以通过参数就能判断具体实现类,不需要事先实例化,更加灵活多变。
The Mechanism of Dynamic Proxy
谈完了什么是动态代理,现在就可以来了解一下动态代理是怎么实现的了。要去知道Jdk动态代理是怎么实现的还得去源码中找。
看看Proxy#newProxyInstance
的实现。123456789101112131415161718192021222324252627282930313233343536373839public 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);}/** 找缓存里有没有Proxy对象,没有就生成一个.*/Class<?> cl = getProxyClass0(loader, interfaces);/** Invoke its constructor with the designated invocation handler.* 用指定的handler来调用构造方法*/try {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 permissionreturn AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {return newInstance(cons, ih);}});} else {return newInstance(cons, ih);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString());}}
从上面的代码中不难看出其核心在于获取一个代理类对象。得到类对象后再通过反射去创建代理对象实例。看看getProxyClass0
怎么实现的。
呃…好吧!这个方法是从一个cache对象中去拿Proxy类对象的。所以我们还是不知道代理类对象是怎么产生的。不过这里有一点有点意思,就是代理类实现的接口数不能超过65535个。看到这个数字是否很惊喜或者也有点意外?关于65535我特意去查了一下Wiki)。这是Java语言机制导致的。当然没有哪个变态去实现这么多个接口吧。
既然从cache中去找,那么看看这个cache到底是何方神圣。
具体到这个cache是怎么实现的就不去纠结了,因为纠结也没用(API文档中没有这个类的解释,因为这个类的修饰是package-private的而不是public)。仔细以看有一个ProxyClassFactory
对象作为参数传给这个cache了。这下自貌似有搞头了。看看这个类的名字就很爽—代理类工厂嘛。实际上这是Proxy的一个静态内部类。
唉,感觉看了半天发现又被绕进去了。没办法,继续看看ProxyGenerator
这个类怎么去实现的。这个是Jdk私有的,但是可以反编译查看。
其中,这个参数的定义是酱紫的。
所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。
加上这段代码后执行main
会报这样的错。
解决办法是在工程根路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,才能够正常运行并保存class文件内容。
看看反编译后的代码。
从生成出来的这个类中可以看到
- 这个类继承自
Proxy
实现了代理接口。所以JDK动态代理只能对接口进行代理,而不能对实现类进行代理。这是Java语言不能多继承导致的。 - 构造方法的参数是
InvocationHandler
。这个参数是由我们调用Proxy#newProxyInstance
方法传进去的。 - 重写了Object类的
equals
、hashCode
、toString
,它们都只是简单的调用了InvocationHandler
的invoke
方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
从这里可以联想到Spring AOP的机制和这个原理其实是一样的,可能实现会比这个复杂的多。Summary
- 使用JDK实现动态代理需要实现
InvacationHandler
接口,使用Proxy#newProxyInstacne
返回代理对象。 - JDK动态代理的机制是通过在程序运行时动态地去生成字节码文件,然后加载到内存生成实例。
- JDK动态代理只能对接口进行代理,不能对实现类代理。因为Java语言不支持多继承。要想对实现类进行代理可以使用Cglib来实现。