使用动态代理
下面的代码演示了用动态代理来创建一个BuilderRobot时所必需的类。注意我们创建的这个 BuilderRobotInvocationHandler类甚至根本没有实现Robot接口。相反,它实现了 java.lang.reflect.InvocationHandler,只提供了一个invoke方法。代理对象上的任何方法调用都要通过这一方法进行。观察invoke的主体,我们发现它会检查准备调用的方法的名称。如果这个名称是workOn,第二个参数就切换成一个非破坏性的工具。
然而,我们得到的仍然只是一个具有invoke方法的InvocationHandler,而不是我们真正想要的Robot对象。动态代理真正的魅力要到创建实际的Robot实例时才能反映出来。在源代码的任何地方,我们都没有定义一个Robot包装器或者子类。虽然如此,我们最终仍能获得一个动态创建的类,它通过调用BuilderRobotInvocationHandler的静态方法createBuilderRobot中的代码片断,从而实现了Robot接口,并集成了Builder工具过滤器。
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class BuilderRobotInvocationHandler implements InvocationHandler {
private Robot wrapped;
public BuilderRobotInvocationHandler(Robot r) {
wrapped = r;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("workOn".equals(method.getName())) {
args[1] = Tool.RATCHET;
}
return method.invoke(wrapped, args);
}
public static Robot createBuilderRobot(Robot toWrap) {
return (Robot)(Proxy.newProxyInstance(Robot.class.getClassLoader(),
new Class[] {Robot.class},
new BuilderRobotInvocationHandler(toWrap)));
}
public static final void main(String[] args) {
Robot r = createBuilderRobot(new MyRobot());
r.workOn("scrap", Tool.CUTTING_TORCH);
}
}
createBuilderRobot中的代码表面上很复杂,但它的作用其实很简单,就是告诉Proxy类用一个指定的类加载器来动态创建一个对象,该对象要实现指定的接口(本例为Robot),并用提供的InvocationHandler来代替传统的方法主体。结果对象在一个instanceof Robot测试中返回true,并提供了在实现了Robot接口的任何类中都能找到的方法。
有趣的是,在BuilderRobotInvocationHandler类的invoke方法中,完全不存在对Robot接口的引用。InvocationHandlers并不是它们向其提供了“代理方法实现”的接口所专用的,你完全可以写一个InvocationHandler,并将其作为众多代理类的后端来使用。
