java动态代理
代理是一种设计模式。 当我们想添加或修改现有类的某些功能时,我们创建并使用代理对象。 使用代理对象而不是原始对象。 通常,代理对象具有与原始对象相同的方法,并且在Java代理类中,通常会extend
原始类。 代理具有原始对象的句柄,并可以在该对象上调用方法。
这样,代理类可以以便捷的方式实现许多事情:
- 在方法开始和停止时记录
- 对参数进行额外检查
- 嘲笑原始类的行为
- 实现对昂贵资源的懒惰访问
无需修改该类的原始代码。 (以上列表并不广泛,仅是示例。)
在实际应用中,代理类不会直接实现功能。 遵循单责任原则,代理类仅进行代理,并且实际的行为修改在处理程序中实现。 当调用代理对象而不是原始对象时,代理将决定是否必须调用原始方法或某些处理程序。 处理程序可以执行其任务,也可以调用原始方法。
即使代理模式不仅适用于在运行时创建代理对象和代理Cass的情况,但这在Java中也是一个特别有趣的话题。 在本文中,我将重点介绍这些代理。
这是一个高级主题,因为它需要使用反射类,或者字节代码操作或编译动态生成的Java代码。 或所有这些。 要使新类在运行时还不能用作字节码,将需要生成字节码,并需要一个用于加载字节码的类加载器。 要创建字节码,可以使用cglib或bytebuddy或内置的Java编译器。
当我们考虑代理类及其调用的处理程序时,我们可以理解为什么在这种情况下责任分离很重要。 代理类是在运行时生成的,但是可以将代理类调用的处理程序编码为常规源代码,并沿着整个程序的代码进行编译(编译时)。
最简单的方法是使用java.lang.reflect.Proxy
类,它是JDK的一部分。 该类可以创建一个代理类或直接创建它的一个实例。 Java内置代理的使用很容易。 您需要做的就是实现一个java.lang.InvocationHandler
以便代理对象可以调用它。 InvocationHandler
接口非常简单。 它仅包含一个方法: invoke()
。 invoke()
,参数包含被代理的原始对象,被调用的方法(作为反射Method
对象)和原始参数的对象数组。 示例代码演示了用法:
package proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxyDemo {interface If {void originalMethod(String s);}static class Original implements If {public void originalMethod(String s) {System.out.println(s);}}static class Handler implements InvocationHandler {private final If original;public Handler(If original) {this.original = original;}public Object invoke(Object proxy, Method method, Object[] args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException {System.out.println("BEFORE");method.invoke(original, args);System.out.println("AFTER");return null;}}public static void main(String[] args){Original original = new Original();Handler handler = new Handler(original);If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),new Class[] { If.class },handler);f.originalMethod("Hallo");}}
如果处理程序想要在原始对象上调用原始方法,则它必须有权访问它。 Java代理实现未提供此功能。 您必须自己在代码中将此参数传递给处理程序实例。 (请注意,有一个通常称为proxy
的对象作为参数传递给调用处理程序。这是Java反射动态生成的代理对象,而不是我们要代理的对象。)这样,您绝对可以使用单独的对象每个原始类的处理程序对象,或者使用某种共享对象,这些共享对象恰好知道某种方式(如果有任何要调用的方法)来调用哪个原始对象。
作为一种特殊情况,您可以创建一个调用处理程序和一个没有任何原始对象的接口的代理。 甚至不需要任何类来实现源代码中的接口。 动态创建的代理类将实现该接口。
如果要代理的类未实现接口,该怎么办? 在这种情况下,您必须使用其他代理实现。 我们将在下周讨论一下。
翻译自: https://www.javacodegeeks.com/2016/01/java-dynamic-proxy.html
java动态代理