大家好!
好吧,在新年和第十波开始之前,
“ Java开发人员”完全是在开玩笑。 因此,我们有一个公开的课程正在准备发布,而今天的文章将为您学习动态Java代理:它是什么,何时以及如何在代码中使用它。
什么是代理?代理是一种设计模式。 我们创建并使用它来添加和更改现有类的功能。 在这种情况下,将使用代理对象而不是原始对象。 通常,它使用与原始方法相同的方法,并且在Java代理类中扩展了原始方法。 代理可以在源对象上调用方法,因为它具有原始描述符。
因此,代理类方便地实现了许多事情:
- 记录方法的开始和停止;
- 额外的参数检查;
- 模仿源类的行为;
- 实施昂贵资源的延迟初始化;

所有这些都发生在不更改原始类代码的情况下。 完整列表不限于上述示例,它们只是其中的一小部分。
实际上,代理类不会直接实现功能。 遵循唯一责任的原则,代理类仅直接执行代理,并且在处理程序中实现行为更改。 当调用代理对象而不是原始对象时,代理决定是调用原始方法还是调用某些处理程序。 处理程序既可以执行自己的任务,也可以引用原始方法。
尽管代理模式不仅用于在运行时中创建代理对象和类,但在Java中这是一个特别有趣的主题。 在本文中,我将重点介绍此类代理。
这是一个复杂的主题,需要使用反射类,操作字节码或编译动态生成的Java代码。 或一次全部。 为了防止新类在运行时作为字节码可用,将需要生成的字节码和类加载器来加载字节码。 要创建字节码,请使用
cglib ,
bytebuddy或内置的Java编译器。
在我们的案例中,职责分离的重要性变得很明显,您只需要考虑代理类及其调用的处理程序。 代理类是在运行时生成的,但是可以将其调用的处理程序添加到常规源代码中,并与程序的其余部分一起编译。
如何在我们的代码中使用它?
最简单的方法是使用JDK的一部分
java.lang.reflect.Proxy
。 此类可以直接创建代理类或其实例。 使用内置在Java中的代理非常简单。 您需要做的就是实现
java.lang.InvocationHandler
以便代理对象可以调用它。
InvocationHandler
接口非常简单,并且仅包含一种方法:
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代理实现未提供的内容。 您需要将参数自己传递给代码中的处理程序实例。 (请注意对象(通常称为代理),它作为参数传递给被调用的处理程序。这是Java动态生成的代理对象,而不是我们要代理的对象。)因此,您可以将其单独使用每个源类的处理程序对象以及一个知道如何调用原始对象的通用对象(如果有)。
在特殊情况下,您可以在没有原始对象的情况下创建呼叫处理程序和代理接口。 此外,不需要用于在源代码中实现接口的类。 它是由动态创建的代理类实现的。
如果代理类未实现该接口,则应考虑使用其他代理实现。
结束
等待您的意见和问题。 像往常一样,在这里,或者您可以前往
Vitaly 开放一天 。