“嗨,阿米戈。”

“你好,瑞希。”

“今天我将向您解释一个非常有趣的新话题:动态代理”

“Java 有几种方法可以改变特定类的功能……”

“第一个方法,传承。”

“更改类行为的最简单方法是创建一个继承原始(基)类的新类,并覆盖其方法。然后,使用派生类而不是原始类。例如:”

Reader reader = new UserCustomReader();

“第二种方法是使用包装类。”

" BufferedReader是这类类的一个例子。首先,它继承了Reader。换句话说,它可以代替 Reader 使用。其次,它将所有调用重定向到原始Reader对象,必须传递给 BufferedReader 对象的构造函数。 例如:”

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

“第三种方法是创建一个动态代理(Proxy)。”

“Java 中有一个特殊的类 (java.lang.reflect.Proxy),它实际上允许您在程序执行期间(动态地)构造一个对象,而无需为其创建单独的类。”

“这很容易做到:”

Reader reader = (Reader)Proxy.newProxyInstance();

“这对我来说已经是新鲜事了!”

“但是当然,我们不需要没有方法的对象。我们需要对象有方法,我们需要它们来做我们想做的事情。Java为此使用了一个特殊的接口,称为 InvocationHandler ,它可以拦截所有方法调用与代理对象相关联。只能使用接口创建代理对象。”

Invoke – 是主要任务是简单地调用某些方法的方法或类的标准名称。”

Handler – 是处理某些事件的类的标准名称。例如,处理鼠标点击的类将称为 MouseClickHandler 等。”

“InvocationHandler 接口有一个调用方法,所有对代理对象的调用都指向该方法。例如:”

代码
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

“调用reader.close ()方法时,会调用invoke方法,屏幕会显示‘是!’”

》所以,我们声明了一个CustomInvocationHandler类,并实现了InvocationHandler接口及其invoke方法,当invoke方法被调用时,显示'yes!'。然后我们创建了一个CustomInvocationHandler对象,创建时将其传递给newProxyInstance方法一个代理对象。”

“是的,都没错。”

“这是一个非常强大的工具。通常,创建这些代理是为了模拟在另一台计算机上物理运行的程序中的对象。 或者控制访问。”

“您可以在这个方法中检查当前用户的权限、处理错误、记录错误等等。”

“这是一个示例,其中 invoke 方法还调用原始对象的方法:”

代码
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

“这个例子有两个特点。”

“首先,将 «原始» Reader对象传递给构造函数,并将对它的引用保存在 CustomInvocationHandler 中

“其次,我们在 invoke 方法中再次调用相同的方法,但这次是在 «original» 对象上。”

“啊。换句话说,这最后一行调用了相同的方法,但在原始对象上:”

return method.invoke(readerOriginal, args);

“是的。”

“我不会说它非常明显,但它仍然是可以理解的。或者看起来是这样。”

“太好了。然后还有一件事。在 newProxyInstance 方法中,您需要传递更多一些内务信息来创建代理对象。但是由于我们没有创建巨大的代理对象,因此可以很容易地从原始类本身获取此信息。 “

“这是另一个例子:”

代码
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

“啊。ClassLoader 和接口列表。这是来自 Reflection 的东西,不是吗?”

“是的。”

“我明白了。好吧,我想如果我需要的话,我可以创建一个原始的、超级简单的代理对象。”

“那你去看看迪亚哥。”