diff --git a/docs/book/19-Type-Information.md b/docs/book/19-Type-Information.md index 43a27d5c..65c5f0ac 100644 --- a/docs/book/19-Type-Information.md +++ b/docs/book/19-Type-Information.md @@ -1405,6 +1405,203 @@ java ShowMethods ShowMethods ## 动态代理 +*代理*是基本的设计模式之一。它是你插入的对象,代替“真实”对象以提供其他或不同的操作---这些操作通常涉及到与“真实”对象的通信,因此代理通常充当中间对象。这是一个简单的示例,显示代理的结构: + +```java +// typeinfo/SimpleProxyDemo.java + +interface Interface { + void doSomething(); + void somethingElse(String arg); +} + +class RealObject implements Interface { + @Override + public void doSomething() { + System.out.println("doSomething"); + } + @Override + public void somethingElse(String arg) { + System.out.println("somethingElse " + arg); + } +} + +class SimpleProxy implements Interface { + private Interface proxied; + SimpleProxy(Interface proxied) { + this.proxied = proxied; + } + @Override + public void doSomething() { + System.out.println("SimpleProxy doSomething"); + proxied.doSomething(); + } + @Override + public void somethingElse(String arg) { + System.out.println( + "SimpleProxy somethingElse " + arg); + proxied.somethingElse(arg); + } +} + +class SimpleProxyDemo { + public static void consumer(Interface iface) { + iface.doSomething(); + iface.somethingElse("bonobo"); + } + public static void main(String[] args) { + consumer(new RealObject()); + consumer(new SimpleProxy(new RealObject())); + } +} +/* Output: +doSomething +somethingElse bonobo +SimpleProxy doSomething +doSomething +SimpleProxy somethingElse bonobo +somethingElse bonobo +*/ +``` + +因为`consumer()`接受`Interface`,所以它不知道获得的是`RealObject`还是`SimpleProxy`,因为两者都实现了`Interface`。 +但是,在客户端和`RealObject`之间插入的`SimpleProxy`执行操作,然后在`RealObject`上调用相同的方法。 + +当你希望将额外的操作与“真实对象”做分离时,代理可能会有所帮助,尤其是当你想要轻松地启用额外的操作时,反之亦然(设计模式就是封装变更---所以你必须改变一些东西以证明模式的合理性)。例如,如果你想跟踪对`RealObject`中方法的调用,或衡量此类调用的开销,该怎么办?这不是你要写入到程序中的代码,而且代理使你可以很轻松地添加或删除它。 + +Java的*动态代理*更进一步,不仅动态创建代理对象而且动态处理对代理方法的调用。在动态代理上进行的所有调用都被重定向到单个*调用处理程序*,该处理程序负责发现调用的内容并决定如何处理。这是`SimpleProxyDemo.java`使用动态代理重写的例子: + +```java +// typeinfo/SimpleDynamicProxy.java +import java.lang.reflect.*; + +class DynamicProxyHandler implements InvocationHandler { + private Object proxied; + DynamicProxyHandler(Object proxied) { + this.proxied = proxied; + } + @Override + public Object + invoke(Object proxy, Method method, Object[] args) + throws Throwable { + System.out.println( + "**** proxy: " + proxy.getClass() + + ", method: " + method + ", args: " + args); + if(args != null) + for(Object arg : args) + System.out.println(" " + arg); + return method.invoke(proxied, args); + } +} + +class SimpleDynamicProxy { + public static void consumer(Interface iface) { + iface.doSomething(); + iface.somethingElse("bonobo"); + } + public static void main(String[] args) { + RealObject real = new RealObject(); + consumer(real); + // Insert a proxy and call again: + Interface proxy = (Interface)Proxy.newProxyInstance( + Interface.class.getClassLoader(), + new Class, + new DynamicProxyHandler(real)); + consumer(proxy); + } +} +/* Output: +doSomething +somethingElse bonobo +**** proxy: class $Proxy0, method: public abstract void +Interface.doSomething(), args: null +doSomething +**** proxy: class $Proxy0, method: public abstract void +Interface.somethingElse(java.lang.String), args: +[Ljava.lang.Object;@6bc7c054 + bonobo +somethingElse bonobo +*/ +``` + +可以通过调用静态方法`Proxy.newProxyInstance()`来创建动态代理,该方法需要一个类加载器(通常可以从已加载的对象中获取),希望代理实现的接口列表(不是类或抽象类),以及接口`InvocationHandler`的一个实现。动态代理会将所有调用重定向到调用处理程序,因此通常为调用处理程序的构造函数提供对“真实”对象的引用,以便一旦执行中介任务便可以转发请求。 + +`invoke()`方法被传递给代理对象,以防万一你必须区分请求的来源---但是在很多情况下都无需关心。但是,在`invoke()`内的代理上调用方法时要小心,因为通过接口的调用是通过代理重定向的。 + +通常执行代理操作,然后使用`Method.invoke()`传递必要的参数将请求转发给代理对象。这在一开始看起来是有限制的,好像你只能执行一般的操作。但是,可以过滤某些方法调用,同时传递其他方法调用: + +```java +// typeinfo/SelectingMethods.java +// Looking for particular methods in a dynamic proxy +import java.lang.reflect.*; + +class MethodSelector implements InvocationHandler { + private Object proxied; + MethodSelector(Object proxied) { + this.proxied = proxied; + } + @Override + public Object + invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if(method.getName().equals("interesting")) + System.out.println( + "Proxy detected the interesting method"); + return method.invoke(proxied, args); + } +} + +interface SomeMethods { + void boring1(); + void boring2(); + void interesting(String arg); + void boring3(); +} + +class Implementation implements SomeMethods { + @Override + public void boring1() { + System.out.println("boring1"); + } + @Override + public void boring2() { + System.out.println("boring2"); + } + @Override + public void interesting(String arg) { + System.out.println("interesting " + arg); + } + @Override + public void boring3() { + System.out.println("boring3"); + } +} + +class SelectingMethods { + public static void main(String[] args) { + SomeMethods proxy = + (SomeMethods)Proxy.newProxyInstance( + SomeMethods.class.getClassLoader(), + new Class, + new MethodSelector(new Implementation())); + proxy.boring1(); + proxy.boring2(); + proxy.interesting("bonobo"); + proxy.boring3(); + } +} +/* Output: +boring1 +boring2 +Proxy detected the interesting method +interesting bonobo +boring3 +*/ +``` + +在这个示例里,我们只是在寻找方法名,但是你也可以寻找方法签名的其他方面,甚至可以搜索特定的参数值。 + +动态代理不是你每天都会使用的工具,但是它可以很好地解决某些类型的问题。你可以在Erich Gamma等人的*设计模式*中了解有关*代理*和其他设计模式的更多信息。 (Addison-Wesley,1995年),以及[设计模式](./25-Patterns.md)一章。 ## Optional类