长沙网站优化收费,设计素材网站排行榜,html5网站开发教学,手机网站怎么布局有时需要拦截某些方法调用#xff0c;以便每次调用被拦截方法时都执行自己的逻辑。 如果您不属于Java EE的CDI领域#xff0c;并且不想使用诸如Aspectj之类的AOP框架#xff0c;那么您将有一个简单而有效的替代方法。 从1.5版开始#xff0c;JDK附带了类java.lang.reflect… 有时需要拦截某些方法调用以便每次调用被拦截方法时都执行自己的逻辑。 如果您不属于Java EE的CDI领域并且不想使用诸如Aspectj之类的AOP框架那么您将有一个简单而有效的替代方法。 从1.5版开始JDK附带了类java.lang.reflect.Proxy该类使您可以为给定的接口创建动态代理。 每次应用程序调用代理上的方法时都会调用位于动态创建的类后面的InvocationHandler。 因此您可以在调用某个框架或库的代码之前动态控制执行什么代码。 在JDK的代理实现旁边像javassist或cglib这样的字节码框架提供了类似的功能。 在这里您甚至可以对现有的类进行子类化并确定要转发给超类的实现的方法以及要拦截的方法。 当然这会带来项目依赖的另一个库的负担并且可能需要不时更新而运行时环境中已经包含了JDK的Proxy实现。 因此让我们仔细看看并尝试这三种选择。 为了将javassist和cglib的代理与JDK实现进行比较我们需要一个由简单类实现的接口因为JDK机制仅支持接口而没有子类 public interface IExample {void setName(String name);
}public class Example implements IExample {private String name;public String getName() {return name;}public void setName(String name) {this.name name;}
} 为了将代理上的方法调用委托给某个实际对象我们创建了上面的Example类的实例并通过最终声明的变量在InvocationHandler中对其进行了调用 final Example example new Example();
InvocationHandler invocationHandler new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(example, args);}
};
return (IExample) Proxy.newProxyInstance(JavaProxy.class.getClassLoader(), new Class[]{IExample.class}, invocationHandler); 从代码示例中您可以看到代理的创建非常简单调用静态方法newProxyInstance并提供ClassLoader应由代理实现的接口数组以及InvocationHandler接口的实例。 为了演示起见我们的实现仅转发我们之前创建的Example实例。 但是在现实生活中您当然可以执行更高级的操作以评估例如方法名称或其参数。 现在我们来看一下使用javassist完成相同操作的方式 ProxyFactory factory new ProxyFactory();
factory.setSuperclass(Example.class);
Class aClass factory.createClass();
final IExample newInstance (IExample) aClass.newInstance();
MethodHandler methodHandler new MethodHandler() {Overridepublic Object invoke(Object self, Method overridden, Method proceed, Object[] args) throws Throwable {return proceed.invoke(newInstance, args);}
};
((ProxyObject)newInstance).setHandler(methodHandler);
return newInstance; 在这里我们有一个ProxyFactory它想知道应该为哪个类创建子类。 然后我们让ProxyFactory创建一个整个类该类可以根据需要多次重用。 这里的MethodHandler与InvocationHandler相似后者是为实例的每次方法调用而调用的。 在这里我们再次将调用转发到之前创建的Example实例。 最后但并非最不重要的一点让我们看一下cglib的代理 final Example example new Example();
IExample exampleProxy (IExample) Enhancer.create(IExample.class, new MethodInterceptor() {Overridepublic Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {return method.invoke(example, args);}
});
return exampleProxy; 在cglib世界中我们有一个Enhancer类可用于通过MethodInterceptor实例实现给定的接口。 回调方法的实现看起来与javassist示例中的实现非常相似。 我们只是通过反射API将方法调用转发到Example的现有实例。 既然我们已经看到了三种不同的实现我们还希望评估它们的运行时行为。 因此我们编写了一个简单的单元测试它测量了每个实现的执行时间 Test
public void testPerformance() {final IExample example JavaProxy.createExample();long measure TimeMeasurement.measure(new TimeMeasurement.Execution() {Overridepublic void execute() {for (long i 0; i JavassistProxyTest.NUMBER_OF_ITERATIONS; i) {example.setName(name);}}});System.out.println(Proxy: measure ms);
} 我们选择大量的迭代以便强调JVM并让HotSpot编译器为经常执行的段落创建本机代码。 下表显示了三种实现的平均运行时间 为了完全显示代理实现的影响该图表还显示了对Example对象“无代理”进行标准方法调用的执行时间。 首先我们可以记录下来代理实现比方法本身的普通调用慢大约10倍。 但是我们也注意到三种代理解决方案之间的差异。 令人惊讶的是JDK的Proxy类几乎与cglib实现一样快。 只有javassist的退出时间是cglib的两倍左右。 结论运行时代理易于使用您有不同的处理方式。 JDK的代理仅支持接口代理而javassist和cglib允许您对现有类进行子类化。 代理的运行时行为比标准方法调用慢大约10倍。 三种解决方案在运行时间方面也有所不同。 参考 实现动态代理–来自我们的JCG合作伙伴 Martin Mois在Martins Developer World博客上的比较。 翻译自: https://www.javacodegeeks.com/2014/01/implementing-dynamic-proxies-a-comparison.html