动态代理类是一个在运行时由开发人员所指定的一列接口的实现。动态代理接口是一种由代理类实现的接口,并且是一个java.lang.reflect.Proxy类的实例。每一个代理实例都与一个调用处理器对象相联,这个调用处理器实现了java.lang.reflect.InvocationHandler接口。在代理实例上的一个方法调用是通过其中之一的代理接口被转发到与这个代理实例相联的调用处理的invoke方法上。一个java.lang.reflect.Method对象会决定那一个方法会被调用,一个类型为java.lang.Object的数组包含调用的参数。调用处理器会适当地解码方法的调用(encoded method invocation as appropriate),并且它(调用处理器)的返回结果被作为在代理实例上方法调用返回的结果而返回。
例如,我们已有和装饰器模式中一文中一样的接口IMyBusinessObject和业务类MyBusinessObject。现在当我们使用动态代理时,我必须编写一个调用处理器,因为java.lang.reflect.Proxy类将会用到它。
MyDebugInvocationHandler类看起来像下面那样:
public class MyDebugInvocationHandler implements InvocationHandler {
private Object target ;
public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Going to execute method :"+method.getName());
Object retObject = method.invoke(target, args);
System.out.println("After execute method :"+method.getName());
return retObject;
}
}
在上面的例子中,invoke ()方法是很重要的,它是被java.lang.reflect.Proxy类所调用的。在这个方法里面,我们执行了一些额外的处理,然后转至真正的目标对象的处理(在这个例子中,是实例MyBusinessObject)。
因此我们的客户端应作如下编码:
IMyBusinessObject bo = new MyBusinessObject();
MyDebugInvocationHandler aMyDebugInvocationHandler = new MyDebugInvocationHandler();
aMyDebugInvocationHandler.setTarget(bo);
IMyBusinessObject proxyObject = (IMyBusinessObject) Proxy
.newProxyInstance(IMyBusinessObject.class.getClassLoader(),
new Class[] { IMyBusinessObject.class },
aMyDebugInvocationHandler);
System.out.println(proxyObject.doExecute("Hello World"));
输出结果:
Going to execute method :doExecute
Here in MyBusinessObject doExecute: input :Hello World
After execute method :doExecute
Hello World
在上面的代码中,我分别创建一个MyBusinessObject实例和一个MyDebugInvocationHandler实例。我们在MyDebugInvocationHandler中设定目标对象为MyBusinessObject。因此当invoke()方法被调用时,它能够把调求发向正确的目标。然后,我们使用java.lang.reflect.Proxy去创建一个IMyBusinessObject接口的代理对象。既然invoke()方法会处理java.lang.reflect.Mehtod类的生成并且其中没有特定的业务接口的方法,通常这些特定的业务接口的方法在每一个业务接口分别编写个业调用处理器是必须的,知道这一点是很重要的。还有,如果我们想实现一些横跨所有业务接口的横切面(cross-cutting aspect),我们不必实现在业务接口中定义的所有业务方法。例如,为了在我们的业务方法中实现安全性,我们仅仅只须在一个地方编写一个方法去实现安全逻辑,这个安全方法我们将以通用的方法编写。
如果我们想在链中增加多个处理器,我们必须创建另一个调用处理器。然后在新定义的处理器中的setTarget()中我们把它设定为链中的前一个代理对象,而不是设定为MyBusinessObject对象。另一个调用处理器代码如下:
public class MyAnotherInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out
.println("AnotherConcreteDecorator: Going to execute method : doExecute");
if (method.getName().equals("doExecute") && args != null
&& args.length >= 1) {
if (args[0] instanceof String) {
args[0] = args[0] + " Modified by MyAnotherInterceptor";
}
}
Object temp = method.invoke(target, args);
System.out
.println("AnotherConcreteDecorator: After execute method : doExecute");
return temp;
}
}
客户端代码如下:
IMyBusinessObject bo = new MyBusinessObject();
MyDebugInvocationHandler aMyDebugInvocationHandler = new MyDebugInvocationHandler();
aMyDebugInvocationHandler.setTarget(bo);
IMyBusinessObject proxyObject = (IMyBusinessObject) Proxy
.newProxyInstance(IMyBusinessObject.class.getClassLoader(),
new Class[] { IMyBusinessObject.class },
aMyDebugInvocationHandler);
MyAnotherInvocationHandler aMyAnotherInvocationHandler = new MyAnotherInvocationHandler();
aMyAnotherInvocationHandler.setTarget(proxyObject);
IMyBusinessObject nextProxyObject = (IMyBusinessObject) Proxy
.newProxyInstance(IMyBusinessObject.class.getClassLoader(),
new Class[] {IMyBusinessObject.class},
aMyAnotherInvocationHandler);
System.out.println(nextProxyObject.doExecute("Hello World"));
输出结果:
AnotherConcreteDecorator: Going to execute method : doExecute
Going to execute method :doExecute
Here in MyBusinessObject doExecute: input :Hello World Modified by MyAnotherInterceptor
After execute method :doExecute
AnotherConcreteDecorator: After execute method : doExecute
Hello World Modified by MyAnotherInterceptor
从上面的例子我们可以看出,用动态代理比静态装饰链用更少的代码为业务对象增加额外的行为。但是,如果我们像上面一样使用动态代理仍然有一些问题:当创建和链化动态代理时你仍然必须编写大量的代码,并且你还必须处理不如普通的对象创建或工厂方法的对象创建那么友善的代理接口API,还有,当我们需要代理我们的业务对象时,在多个位置重复一些代码并不是一个好主意。
下一篇博客将试图去解决这些问题。我将会编写一个暴露简单API的通用代理工厂(在其中隐藏代理对象的创建和链化),但仍可提供动态代理的扩展性。