在上几周的时间里我展示了如何用Java反射API和cglib创建代理对象。本文将演示如何用djcproxy完成同样的东西。
天了噜,你又来了,又是proxy的实现!
写这样proxy除了出于我的私心之外还有什么意义呢?这个proxy用Java编写并且生成可以检验的Java代码,它可以在运行的时候编译并加载生成Java类,这一点很有用,但是最主要的优势就是你可以轻松并深层次地了解一个动态proxy是如何工作的。至少比去啃直接创建字节码的cglib的源码要简单点。
你可以从 github 上获取源码或者只是添加maven的dependency到你的pom。
<dependency> <groupId>com.javax0</groupId> <artifactId>djcproxy</artifactId> <version>2.0.3</version> </dependency>
然后就可以用下面的代码:
class A { public int method() { return 1; } } class Interceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy mproxy) throws Exception { if (method.getName().equals("toString")) { return "interceptedToString"; } return 0; } } ... A a = new A(); ProxyFactory<A> factory = new ProxyFactory<>(); A s = factory.create(a, new Interceptor());
这段代码可以在位于Github上的项目的测试里面找到. 这是一个编辑过的简短版本,容易出错。
class A是要代理的类,在我们想创建一个新的代理对象的时候就创建了一个已经存在的对象的代理。这一点是异于反射或者cglib的。假如用cglib,创建代理对象时会把被代理的对象包含进去,这不是一个真正的面向对象思想里的包含(containment),因为代理对象继承了被代理对象的class。cglib并不真的关心你想要拦截的是哪一个class的实例(对象),只要你愿意,你可以注入任何对象的引用到你的拦截器里面。Djcproxy使用了不同的方法,它知道你要拦截的对象并把该对象作为拦截器的参数传递进去。这就是为什么在第20行你要创建A的对象。
这个拦截器实现的MethodInterceptor接口也由该库提供。它只有一个方法:intercept,当代理对象的方法被调用时它就会执行。方法参数为:
obj – 被拦截的对象
method – 拦截的对象上被调用的方法
args – 传递给被拦截的方法的参数。请注意基本类型会被装箱.
mproxy – 方法的代理,可以调用被代理的对象或者仅仅是其它任何相同类型的对象
这就是关于如何使用这个库的一切了。下一件事情就是看看生成了什么东西以便更好的理解代理是如何工作的。知道的深点绝不是坏事,即使你用的是不同的代理。如果你知道你在使用的库的法则,很多时候时候调试或者写出更好的代码会变得容易。
cglib提供了静态工厂方法创建djcproxy对象,但要求要提供一个代理工厂对象 。就像上面代码中的21行。 如果你想要使用它像使用 cglib 一样,你可以在你要用的工厂类中声明一个静态ProxyFactory 字段。这样你可以在代码的不同部分有不同的工厂对象。尽管这样优势很少,但我相信这样比提供工厂方法更简洁。
这个包中的额外的声明让你可以访问源。 你可以插入这些行:
String generatedSource = factory.getGeneratedSource(); System.out.println(generatedSource);
打印生成的代理类经过一些格式转换是这样的:
package com.javax0.djcproxy; class PROXY$CLASS$A extends com.javax0.djcproxy.ProxyFactoryTest.A implements com.javax0.djcproxy.ProxySetter { com.javax0.djcproxy.ProxyFactoryTest.A PROXY$OBJECT = null; com.javax0.djcproxy.MethodInterceptor PROXY$INTERCEPTOR = null; public void setPROXY$OBJECT(java.lang.Object PROXY$OBJECT) { this.PROXY$OBJECT = (com.javax0.djcproxy.ProxyFactoryTest.A) PROXY$OBJECT; } public void setPROXY$INTERCEPTOR(com.javax0.djcproxy.MethodInterceptor PROXY$INTERCEPTOR) { this.PROXY$INTERCEPTOR = PROXY$INTERCEPTOR; } PROXY$CLASS$A() { super(); } private com.javax0.djcproxy.MethodProxy method_MethodProxyInstance = null; @Override public int method() { try { if (null == method_MethodProxyInstance) { method_MethodProxyInstance = new com.javax0.djcproxy.MethodProxy() { public java.lang.Object invoke(java.lang.Object obj, java.lang.Object[] args) throws Throwable { return ((com.javax0.djcproxy.ProxyFactoryTest.A) obj).method(); } }; } return (int) PROXY$INTERCEPTOR.intercept( PROXY$OBJECT, PROXY$OBJECT.getClass().getMethod("method", new Class[]{}), new Object[]{}, method_MethodProxyInstance); } catch (Throwable e) { throw new RuntimeException(e); } } ... other overridden methods deleted ... }
注意,类 A 是类 ProxyFactoryTest 生成的一个静态嵌套类。
有趣的代码是方法重写了方法。(对于使用这样的命名很抱歉。我没有想到更好的名称给这样什么对没做的方法。) 我们跳过这个方法:检查是否存在 MethodProxy 实例如果不存在则创建一个。这个方法的方法实际上是我们定义拦截器对象,它传代理对象,反射对象,参数同时也传代理方法
第一次看到这个名称可能会混淆因为名称中已经有了代理对象。 这里有一个单独的方法去代理原来类的每个方法。这样可以调用原来的方法而不用去反射调用。 这加速了代理的使用。你在cglib里面也可以这样的调用或者类似的机制.
实现有一些流程, 例如,最后那个代理方法真的没有什么优势,同时还有可能影响到多线程代理的执行。 它可以创建代理对象,不仅继承了一个类还是实现了其他接口。 (那些继承类有些可能还没有被实现). 这些实现可以用在一些开源爱好者项目中也可以在github上,这些我将来我可能会写。它们比生产规范代码更加具有示范,教育和证明性的概念项目。如果对于实现你有什么观点或者想法。 请用评论回复我。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务