在过去的几周中,我展示了如何使用Java Reflection API和cglib创建代理对象。 在本文中,我将向您展示如何使用djcproxy做到这一点。
哦,不是,另一个代理实现!
除了我创建此代理的自私事实之外,还要写些什么呢? 关键是这是一个用Java编写的代理,它创建了可以检查的Java代码。 它还可以即时编译和加载创建的Java类,因此它也可以使用,但主要优点是您可以轻松了解动态代理的工作原理。 至少比深入研究cglib的代码要容易得多,后者直接创建字节码。
如何使用它
您可以从github获取源代码,也可以将依赖项添加到项目maven 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 {@Overridepublic 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中的项目测试中找到此代码。 这是一个易于编辑的易于编辑的缩写版本。
类“ A”是原始类,当我们要创建新的代理对象时,我们将创建一个已存在对象的代理。 这不同于反射或屈服。 如果使用cgilib,您将创建一个代理对象,它“包含”原始对象。 从OO术语来看,它并不是真正的包含,因为代理类扩展了原始类。 但是,由于此扩展,代理对象也是原始类的实例。 Cgilib并不真正在乎您要拦截哪个类实例(对象)。 如果需要,可以将对任何对象实例的引用注入到拦截器中。 Djcproxy使用了一种不同的方法,它可以为您做到这一点,在拦截器中,您将获得此对象作为参数传递。 这就是为什么您必须在第20行实例化该对象的原因。
Interceptor
实现了库中也提供的接口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;@Overridepublic 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
的静态嵌套类。
有趣的代码是方法method()
的重载。 (对不起这个名字。对于一个什么都不做的方法,我有一个更好的名字。我不MethodProxy
。)让我们跳过该部分,检查该方法是否已经存在MethodProxy
实例,如果缺少则创建一个。 方法method()
实际上会调用我们定义的拦截器对象,并传递代理对象,反射方法对象,参数以及方法代理。
什么是方法代理
该名称可能首先引起混淆,因为我们已经有一个“对象”代理。 原始类的每个方法都有一个单独的方法代理。 这些可用于调用原始方法,而无需进行反射调用。 这加快了代理的使用。 您还可以在cglib中找到此调用和类似的机制。
笔记
该实现有一些流程,例如,后方法代理实例化实际上没有优势,但同时在代理的多线程执行情况下可能会受到损害。 还可以创建一个不仅扩展类而且实现任意接口的代理对象(也许某些扩展类甚至没有实现)。 该实现在github上也有一些其他业余爱好开源项目中使用,我将来可能会写有关该项目的信息。 它们比生产代码更具说明性,教育性和概念证明性项目。 如果您对实施,想法或任何意见有任何意见,请给我您的意见。
翻译自: https://www.javacodegeeks.com/2016/02/creating-proxy-object-using-djcproxy.html