委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委 托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继 承,它还使我们可以模拟mixin。
“委托”在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是我们可以通过动态代理来实现委托!代码如下:
以通过动态代理来实现委托
代理模式是一个在面向对象编程中重要而广泛被使用的设计模式。JDK1.3中已经介绍了Proxy,你在 Java使用过Proxy吗?动态代理类实现了在运行时所指定的一组接口。在运行时,通过使用InvocationHandler来提供代理的行为。因 此,Proxy是在JAVA反射包中一个重要的类,并且广泛地用于许多JAVA应用程序中。
Proxy的一个限制是它仅仅接受接口。在某些情况下,你不仅需要使用代理模式来代理接口,而且也需要用它来代理抽象类,甚至具体的类。
本文介绍了动态委托(Dynamic Delegation),它能够创建运行时接口和类的代理。
Proxy概述
在JDK1.3中, java.lang.reflect包增加了Proxy类。它能够创建一个具体类,这个类实现了所有在运行时指定的所有接口。动态生成的类将所有定义在接口中的方法调用重定向到InvocationHandler。
给定两个接口,Idel1和Idel2, Proxy将创建一个IdelProxy类作为这两个接口的代理(为了方便起见,使用IdelProxy作为生成的代理类名字)。图1展现了这种结构:
图1. IdelProxy的类图
下面是相关的简要代码:
委托与代理的比较
Proxy仅仅对接口进行代理。如果想要用Proxy进行接口和类的代理,我们需要做什么呢? java.net的Dunamis project项目介绍了另一种相应的模式—Delegation模式。Delegation使用了与Proxy不同的方法实现。
给定一个名字为TestBean的类,委托类TestBeanDelegation的类图如图2所示:
图2. TestBeanDelegation的类图
TestBeanDelegation 实现了Delegation接口并且继承了TestBean类。它也包含了对TestBean和DelegationInvocationHandler 的引用。所有TestBeanDelegation上的方法调用都是对这两者的委托。
以getName()为例,图3描述了方法调用的顺序图。
图3. TestBeanDelegation.getName()调用的顺序图
相关伪码如下:
动态委托的介绍
动态委托概念来自于Jakarta 字节码工程库 (Byte-Code Engineering Library, BCEL)。它能够分析存在的类,并且对于接口,抽象类,甚至运行时的具体类来说,它能够生成以字节编码委托类。
被委托的接口/类应该满足如下条件:
动态委托最多只能委托一个类,但是能够代理多个接口。
这个限制来自于Java的单继承模式。一个Java类最多只有一个父类。既然生成的委托类把被委托类作为它的父类,那么指定多个被委托类是不合理的。如果没有指定被委托类,那么缺省的父类就是Object。
被委托类应该有一个有限定符public或者protected的缺省构造函数。
委托类会在它自己的构造函数中调用父类的缺省构造函数。
被委托类不能是final,而应该对于它的调用者可见。
Proxy生成的代理类是final的。而动态委托却不能接受这种情况。
动态委托不能委托实现了接口Delegation的任何类。
既然类已经是一个委托类,就没有必要重新委托它。
生成的委托类有下面的特点:
委托类是在运行时产生的,没有类文件
委托类实现了所有继承的接口,扩展了被委托的类。
委托类也实现了Delegation接口。
委托类有一个接受Object实例作为参数的构造函数。
DelegationGenerator 是动态委托的主要类。客户程序能够使用它来生成指定类,接口,对象的委托类和对象。DelegationInvocationHandler是一个定义了 所有委托行为的接口,客户程序的开发人员应该实现这些所有的接口。委托对象能够使用定义在Delegation中的 _getInvocationHandler和_setInvocationHandler方法来访问委托对象中的 DelegationInvocationHandler实例。
练习1。创建一个具体类的委托类
<script type="text/javascript">render_code();</script>
下面的代码,则是作为一个委托的例子,实现Map的功能。
<script type="text/javascript">render_code();</script>
注意:UtilLog仅仅是用于配置log4j属性文件位置,如果log4j.properties就在缺省的运行路径下,则无需单独配置。或者用System.out输出来替代_log输出。
假定存在一个名字为ConcreteClass的具体类:
下面的代码生成ConcreteClass类的委托类
输出为:
Delegation class name =
org.jingle.util.dydelegation.sample.ConcreteClass_Delegation_0
true
DelegationGenerator.getDelegationClass()接受类数组为参数,返回一个Java类,这个类继承了给定类或者实现了给定接口。缺省情况下,生成的委托类和被委托类是在同一个包中。
委托类能够像下面那样被实例化:
练习2:创建一个抽象类的委托类
DelegationGenerator也能够生成一个对于抽象类的具体的委托类
输出:
Delegation class name =
org.jingle.util.dydelegation.sample.AbstractClass_Delegation_0
false
生成的委托类是具体类而不是抽象类。
练习3。创建类和多个接口的委托类
DelegationGenerator.getDelegationClass()能够同时委托一个类和多个接口,生成委托类来委托给定的类和接口。并且,将去除重复的接口。
输出:
true
true
true
生成的委托类扩展了给定的类ConcreteClass,实现了所有的给定接口:Idel1和Idel2。
练习4。创建单个对象的委托对象
根据一个特定的被委托对象,DelegationGenerator能够直接生成一个委托对象。
输出:
Interrupted by SimpleHandler
Hello from ConcreteClass
Hello again from ConcreteClass
Interrupted by SimpleHandler
org.jingle.util.dydelegation.sample.ConcreteClass@ef5502
DummyInvocationHandler 是一个DelegationInvocationHandler虚拟的实现。它总是在方法invokeBefore()中返回true,在方法 invokeAfter()中直接返回输入的值,并且在invokeAfterException()中直接抛出输入异常throwable。带有 DummyInvocationHandler委托对象和被委托对象有相同的动作。
DelegationGenerator.newDelegationInstance()将一个对象和DelegationInvocationHandler实例作为参数。它返回委托对象来委托给定对象。
所有对委托对象的调用方法将被DelegationInvocationHandler实例委托,除了下面的方法:
没有public限定符的方法。
没有final限定符的方法
有static限定符的方法
定义在一个对象类中,除了hashCode(),equals()和toString()而外的方法。
练习5。创建一个Java核心类对象的委托对象
你曾经想过委托存在的Java核心类对象吗?可以像通常所做的那样委托它。
输出
Delegation class = org.jingle.util.dydelegation.Date_Delegation_0
True date = 1099380377665
Delegation date = 1099380376665
当创建一个Java核心类的委托类时,委托类不会和Java核心类在同一个包中,因为Java安全模型不允许用户定义的ClassLoader定义以java开头的包中的类。
DateClassHandler代理在invokeAfter()中的getTime()方法调用,返回的值比正常返回值低1000。
高级用法
练习6。模拟代理行为
委托能够做代理所做的事吗?绝对可以!动态委托覆盖了代理的功能。给定一个适当的委托句柄,它能够模拟Java代理的行为。
输出
Hello from idel1
Hello from idel2
ProxyHandler在方法invokeBefore()中返回false,这意味着在委托对象上的所有方法调用都不会代理原来的对象。与代理Proxy相同,它使用方法invokeAfter()来定义委托行为。
DelegationGenerator.newDelegationInstance()方法有另一个版本。它包含4个参数:
被委托的对象
这可能是null。如果它不是null,它必须是所有给定的类和接口的实例
被委托的类数组
这可能包含多个接口,和一个类。
委托类名字
如果是null,系统将会生成一个名字
一个DelegationInvocationHandler实例,使用它来定义委托的行为。
从输出结果,我们可以看到委托对象是两个接口Idel1和Idel2的实例。它的行为就是定义在句柄中的。
练习7.部分委托
直到现在,我们已经委托了指定对象所有的函数。怎么样来进行对象函数的部分委托呢?
输出
true
true
false
MyDate 扩展了Date类,实现了Idel1和Idel2接口。DelegationGenerator.newDelegationInstance()使用 MyDate实例作为被委托的实例,将委托范围限制在Idel1和Idel2中。换句话说,生成的委托对象是Idel1和Idel2的实例,而不是 Date实例。
结论
Dunamis 项目介绍了动态委托模式来扩展Java代理(Proxy)反射机制。它能够在运行时生成类和接口的委托。本文使用简单的例子简短地介绍了动态委托。在实际 里,许多领域能够使用动态委托,例如单元测试中的mock对象,Java GUI MVC框架,以及其他方面。
资源
Dunamis 项目: dunamis.dev.java.net
下载本文例子程序:例子代码
BCEL: jakarta.apache.org/bcel/index.html
“委托”在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是我们可以通过动态代理来实现委托!代码如下:
以通过动态代理来实现委托
代理模式是一个在面向对象编程中重要而广泛被使用的设计模式。JDK1.3中已经介绍了Proxy,你在 Java使用过Proxy吗?动态代理类实现了在运行时所指定的一组接口。在运行时,通过使用InvocationHandler来提供代理的行为。因 此,Proxy是在JAVA反射包中一个重要的类,并且广泛地用于许多JAVA应用程序中。
Proxy的一个限制是它仅仅接受接口。在某些情况下,你不仅需要使用代理模式来代理接口,而且也需要用它来代理抽象类,甚至具体的类。
本文介绍了动态委托(Dynamic Delegation),它能够创建运行时接口和类的代理。
Proxy概述
在JDK1.3中, java.lang.reflect包增加了Proxy类。它能够创建一个具体类,这个类实现了所有在运行时指定的所有接口。动态生成的类将所有定义在接口中的方法调用重定向到InvocationHandler。
给定两个接口,Idel1和Idel2, Proxy将创建一个IdelProxy类作为这两个接口的代理(为了方便起见,使用IdelProxy作为生成的代理类名字)。图1展现了这种结构:
图1. IdelProxy的类图
下面是相关的简要代码:
Class clazz = Proxy.getProxyClass(
Idel1.class.getClassLoader(),
new Class[] { Idel1.class, Idel2.class });
委托与代理的比较
Proxy仅仅对接口进行代理。如果想要用Proxy进行接口和类的代理,我们需要做什么呢? java.net的Dunamis project项目介绍了另一种相应的模式—Delegation模式。Delegation使用了与Proxy不同的方法实现。
给定一个名字为TestBean的类,委托类TestBeanDelegation的类图如图2所示:
图2. TestBeanDelegation的类图
TestBeanDelegation 实现了Delegation接口并且继承了TestBean类。它也包含了对TestBean和DelegationInvocationHandler 的引用。所有TestBeanDelegation上的方法调用都是对这两者的委托。
以getName()为例,图3描述了方法调用的顺序图。
图3. TestBeanDelegation.getName()调用的顺序图
相关伪码如下:
//The delegation class is a sub-class of the class to be delegated
public class TestBeanDelegation extends TestBean
implements Delegation {
//The object to be delegated
TestBean bean;
//The invocation handler
DelegationInvocationHandler handler;
...
static Method m0 = null;
...
static {
...
try {
m0 = TestBean.class.getMethod("getName",
new Class[] {});
} catch (Exception exception) {
}
...
}
public TestBeanDelegation(Object bean) {
this.bean = (TestBean)bean;
}
public String getName() {
boolean goon = true;
String ret = null;
Throwable t = null;
try {
goon = handler.invokeBefore(bean,
m0, new Object[] {});
if (goon)
try {
ret = bean.getName();
} catch (Throwable throwable) {
t = throwable;
}
if (t != null)
ret = handler.invokeAfterException(bean,
m0, new Object[] {}, t);
else
ret = handler.invokeAfter(bean,
m0, new Object[] { name }, null);
return ret;
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
动态委托的介绍
动态委托概念来自于Jakarta 字节码工程库 (Byte-Code Engineering Library, BCEL)。它能够分析存在的类,并且对于接口,抽象类,甚至运行时的具体类来说,它能够生成以字节编码委托类。
被委托的接口/类应该满足如下条件:
动态委托最多只能委托一个类,但是能够代理多个接口。
这个限制来自于Java的单继承模式。一个Java类最多只有一个父类。既然生成的委托类把被委托类作为它的父类,那么指定多个被委托类是不合理的。如果没有指定被委托类,那么缺省的父类就是Object。
被委托类应该有一个有限定符public或者protected的缺省构造函数。
委托类会在它自己的构造函数中调用父类的缺省构造函数。
被委托类不能是final,而应该对于它的调用者可见。
Proxy生成的代理类是final的。而动态委托却不能接受这种情况。
动态委托不能委托实现了接口Delegation的任何类。
既然类已经是一个委托类,就没有必要重新委托它。
生成的委托类有下面的特点:
委托类是在运行时产生的,没有类文件
委托类实现了所有继承的接口,扩展了被委托的类。
委托类也实现了Delegation接口。
委托类有一个接受Object实例作为参数的构造函数。
DelegationGenerator 是动态委托的主要类。客户程序能够使用它来生成指定类,接口,对象的委托类和对象。DelegationInvocationHandler是一个定义了 所有委托行为的接口,客户程序的开发人员应该实现这些所有的接口。委托对象能够使用定义在Delegation中的 _getInvocationHandler和_setInvocationHandler方法来访问委托对象中的 DelegationInvocationHandler实例。
练习1。创建一个具体类的委托类
代码
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- /*
- * @author Liusheng
- * 实现“委托”模式,用户需要实现InvocationHandler接口;
- * 参考:http://www.uml.org.cn/j2ee/200411036.htm
- */
- public abstract class Delegator implements InvocationHandler {
- //RelegateTo针对每个对象都要生成一个实例,因而非Static的log,代价比较高。
- //protected Log _log = LogFactory.getLog(this.getClass());
- //private static Log _log = LogFactory.getLog(RelegateTo.class);
- //--------------------------------------------
- protected Object obj_orgin = null; //原始对象
- protected Object obj_proxy = null; //代理对象
- //--------------------------------------------
- public Delegator() {
- //空
- }
- public Delegator(Object orgin){
- this.createProxy(orgin);
- }
- //--------------------------------------------
- protected Object createProxy(Object orgin) {
- obj_orgin = orgin;
- obj_proxy = Proxy.newProxyInstance(
- orgin.getClass().getClassLoader(), //加载器
- orgin.getClass().getInterfaces(), //接口集
- this); //委托
- //_log.debug("# 委托代理:"+obj_proxy);
- return obj_proxy;
- }
- protected Object invokeSuper(Method method, Object[] args)
- throws Throwable {
- return method.invoke(obj_orgin, args);
- }
- //--------------实现InvocationHandler接口,要求覆盖------------
- public Object invoke(Object obj, Method method, Object[] args)
- throws Throwable {
- // 缺省实现:委托给obj_orgin完成对应的操作
- if (method.getName().equals("toString")) { //对其做额外处理
- return this.invokeSuper(method, args)+"$Proxy";
- }else { //注意,调用原始对象的方法,而不是代理的(obj==obj_proxy)
- return this.invokeSuper(method, args);
- }
- }
- }
下面的代码,则是作为一个委托的例子,实现Map的功能。
代码
- import java.io.IOException;
- import java.lang.reflect.Method;
- import java.util.Hashtable;
- import java.util.Map;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import com.bs2.core.UtilLog;
- /**
- * @author Liusheng
- * 本代码主要用于演示RelegateTo的使用方法
- */
- public class Delegator4Map extends Delegator {
- private static Log _log = LogFactory.getLog(Delegator4Map.class);
- private Map orginClass = null; //原始对象
- private Map proxyClass = null; //代理对象
- public Map getOrgin() { return orginClass; }
- public Map getProxy() { return proxyClass; }
- public Delegator4Map(Map orgin) {
- super(orgin);
- orginClass = orgin;
- proxyClass = (Map)super.obj_proxy;
- }
- public Object invoke(Object obj, Method method, Object[] args)
- throws Throwable {
- if (method.getName().equals("size")) { //修改close处理逻辑
- _log.debug("原始 size()="+super.invoke(obj, method, args));
- Object res2 = new Integer(-1);
- _log.debug("修改 size()="+res2);
- return res2;
- }else {
- return super.invoke(obj, method, args);
- }
- }
- public static void main(String[] args) throws IOException {
- UtilLog.configureClassPath("resources/log4j.properties", false);
- Delegator4Map rtm = new Delegator4Map(new Hashtable());
- Map m = rtm.getProxy();
- m.size();
- _log.debug("代理:"+m.toString());
- }
- }
注意:UtilLog仅仅是用于配置log4j属性文件位置,如果log4j.properties就在缺省的运行路径下,则无需单独配置。或者用System.out输出来替代_log输出。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1711118
假定存在一个名字为ConcreteClass的具体类:
//ConcreteClass.java
package org.jingle.util.dydelegation.sample;
public class ConcreteClass {
public void hello() {
System.out.println("Hello from ConcreteClass");
}
protected void hello2() {
System.out.println("Hello again from ConcreteClass");
}
}
下面的代码生成ConcreteClass类的委托类
//ConcreteClassTest.java
package org.jingle.util.dydelegation.sample;
import org.jingle.util.dydelegation.DelegationGenerator;
public class ConcreteClassTest {
public static void main(String[] args) {
Class clazz = DelegationGenerator
.getDelegationClass(new Class[] { ConcreteClass.class });
System.out.println("Delegation class name = " +
clazz.getName());
System.out.println(
ConcreteClass.class.isAssignableFrom(clazz));
}
}
输出为:
Delegation class name =
org.jingle.util.dydelegation.sample.ConcreteClass_Delegation_0
true
DelegationGenerator.getDelegationClass()接受类数组为参数,返回一个Java类,这个类继承了给定类或者实现了给定接口。缺省情况下,生成的委托类和被委托类是在同一个包中。
委托类能够像下面那样被实例化:
//object to be delegated
Object obj = ...;
//some concrete invocation handler instance
DelegationInvocationHandler h = ...;
Constructor c = clazz.getConstructor(new Class[] { Object.class });
Object inst = c.newInstance(new Object[] {obj});
((Delegation) inst)._setInvocationHandler(h);
练习2:创建一个抽象类的委托类
DelegationGenerator也能够生成一个对于抽象类的具体的委托类
//AbstractClass.java
package org.jingle.util.dydelegation.sample;
public abstract class AbstractClass {
public abstract void wave();
}
//AbstractClassTest.java
package org.jingle.util.dydelegation.sample;
import java.lang.reflect.Modifier;
import org.jingle.util.dydelegation.DelegationGenerator;
public class AbstractClassTest {
public static void main(String[] args) {
Class clazz = DelegationGenerator
.getDelegationClass(new Class[] { AbstractClass.class });
System.out.println("Delegation class name = " +
clazz.getName());
System.out.println(
Modifier.isAbstract(clazz.getModifiers()));
}
}
输出:
Delegation class name =
org.jingle.util.dydelegation.sample.AbstractClass_Delegation_0
false
生成的委托类是具体类而不是抽象类。
练习3。创建类和多个接口的委托类
DelegationGenerator.getDelegationClass()能够同时委托一个类和多个接口,生成委托类来委托给定的类和接口。并且,将去除重复的接口。
//Idel1.java
package org.jingle.util.dydelegation.sample.bean;
public interface Idel1 {
public void idel1();
}
//Idel2.java
package org.jingle.util.dydelegation.sample.bean;
public interface Idel2 {
public void idel2();
}
//ComplexClassTest.java
package org.jingle.util.dydelegation.sample;
import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;
public class ComplexClassTest {
public static void main(String[] args) {
Class clazz = DelegationGenerator.getDelegationClass(new Class[] {
ConcreteClass.class, Idel1.class, Idel2.class });
System.out.println(
Idel1.class.isAssignableFrom(clazz));
System.out.println(
Idel2.class.isAssignableFrom(clazz));
System.out.println(
ConcreteClass.class.isAssignableFrom(clazz));
}
}
输出:
true
true
true
生成的委托类扩展了给定的类ConcreteClass,实现了所有的给定接口:Idel1和Idel2。
练习4。创建单个对象的委托对象
根据一个特定的被委托对象,DelegationGenerator能够直接生成一个委托对象。
// ConcreteClassTest2.java
package org.jingle.util.dydelegation.sample;
import java.lang.reflect.Method;
import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
public class ConcreteClassTest2 {
public static void main(String[] args) {
ConcreteClass inst = new ConcreteClass();
DelegationInvocationHandler handler =
new SimpleHandler();
ConcreteClass delegation = (ConcreteClass)
DelegationGenerator.newDelegationInstance(inst, handler);
delegation.hello();
delegation.hello2();
System.out.println(delegation.toString());
}
}
class SimpleHandler extends DummyInvocationHandler {
public boolean invokeBefore(Object bean,
Method method, Object[] args)
throws Throwable {
System.out.println("Interrupted by SimpleHandler");
return super.invokeBefore(bean, method, args);
}
}
输出:
Interrupted by SimpleHandler
Hello from ConcreteClass
Hello again from ConcreteClass
Interrupted by SimpleHandler
org.jingle.util.dydelegation.sample.ConcreteClass@ef5502
DummyInvocationHandler 是一个DelegationInvocationHandler虚拟的实现。它总是在方法invokeBefore()中返回true,在方法 invokeAfter()中直接返回输入的值,并且在invokeAfterException()中直接抛出输入异常throwable。带有 DummyInvocationHandler委托对象和被委托对象有相同的动作。
DelegationGenerator.newDelegationInstance()将一个对象和DelegationInvocationHandler实例作为参数。它返回委托对象来委托给定对象。
所有对委托对象的调用方法将被DelegationInvocationHandler实例委托,除了下面的方法:
没有public限定符的方法。
没有final限定符的方法
有static限定符的方法
定义在一个对象类中,除了hashCode(),equals()和toString()而外的方法。
练习5。创建一个Java核心类对象的委托对象
你曾经想过委托存在的Java核心类对象吗?可以像通常所做的那样委托它。
//DateTest.java
package org.jingle.util.dydelegation.sample;
import java.lang.reflect.Method;
import java.util.Date;
import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
public class DateTest {
public static void main(String[] args) {
Date date = new Date();
DelegationInvocationHandler handler =
new DateClassHandler();
Date delegation = (Date) DelegationGenerator
.newDelegationInstance(date, handler);
System.out.println("Delegation class = " +
delegation.getClass().getName());
System.out.println("True date = " +
date.getTime());
System.out.println("Delegation date = " +
delegation.getTime());
}
}
class DateClassHandler extends DummyInvocationHandler {
public Object invokeAfter(Object bean,
Method method, Object[] args,
Object result) throws Throwable {
if (method.getName().equals("getTime")) {
return new Long(((Long)result).longValue() - 1000);
}
return super.invokeAfter(bean, method, args, result);
}
}
输出
Delegation class = org.jingle.util.dydelegation.Date_Delegation_0
True date = 1099380377665
Delegation date = 1099380376665
当创建一个Java核心类的委托类时,委托类不会和Java核心类在同一个包中,因为Java安全模型不允许用户定义的ClassLoader定义以java开头的包中的类。
DateClassHandler代理在invokeAfter()中的getTime()方法调用,返回的值比正常返回值低1000。
高级用法
练习6。模拟代理行为
委托能够做代理所做的事吗?绝对可以!动态委托覆盖了代理的功能。给定一个适当的委托句柄,它能够模拟Java代理的行为。
// ProxyTest.java
package org.jingle.util.dydelegation.sample;
import java.lang.reflect.Method;
import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;
public class ProxyTest {
public static void main(String[] args) {
DelegationInvocationHandler handler = new ProxyHandler();
Object delegation =
DelegationGenerator.newDelegationInstance(null,
new Class[] { Idel1.class, Idel2.class },
null, handler);
((Idel1) delegation).idel1();
((Idel2) delegation).idel2();
}
}
class ProxyHandler extends DummyInvocationHandler {
public boolean invokeBefore(Object bean,
Method method, Object[] args)
throws Throwable {
return false;
}
public Object invokeAfter(Object bean,
Method method, Object[] args,
Object result) throws Throwable {
String name = method.getName();
if (name.equals("idel1"))
System.out.println("Hello from idel1");
else if (name.equals("idel2"))
System.out.println("Hello from idel2");
return super.invokeAfter(bean, method, args, result);
}
}
输出
Hello from idel1
Hello from idel2
ProxyHandler在方法invokeBefore()中返回false,这意味着在委托对象上的所有方法调用都不会代理原来的对象。与代理Proxy相同,它使用方法invokeAfter()来定义委托行为。
DelegationGenerator.newDelegationInstance()方法有另一个版本。它包含4个参数:
被委托的对象
这可能是null。如果它不是null,它必须是所有给定的类和接口的实例
被委托的类数组
这可能包含多个接口,和一个类。
委托类名字
如果是null,系统将会生成一个名字
一个DelegationInvocationHandler实例,使用它来定义委托的行为。
从输出结果,我们可以看到委托对象是两个接口Idel1和Idel2的实例。它的行为就是定义在句柄中的。
练习7.部分委托
直到现在,我们已经委托了指定对象所有的函数。怎么样来进行对象函数的部分委托呢?
//MyDate.java
package org.jingle.util.dydelegation.sample.bean;
import java.util.Date;
public class MyDate extends Date implements Idel1, Idel2 {
public void idel1() {
}
public void idel2() {
}
}
// MyDateTest.java
package org.jingle.util.dydelegation.sample;
import java.util.Date;
import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;
import org.jingle.util.dydelegation.sample.bean.MyDate;
public class MyDateTest {
public static void main(String[] args) {
MyDate inst = new MyDate();
DelegationInvocationHandler handler =
new DummyInvocationHandler();
Object delegation =
DelegationGenerator.newDelegationInstance(inst,
new Class[] { Idel1.class, Idel2.class },
null, handler);
System.out.println(delegation instanceof Idel1);
System.out.println(delegation instanceof Idel2);
System.out.println(delegation instanceof Date);
}
}
输出
true
true
false
MyDate 扩展了Date类,实现了Idel1和Idel2接口。DelegationGenerator.newDelegationInstance()使用 MyDate实例作为被委托的实例,将委托范围限制在Idel1和Idel2中。换句话说,生成的委托对象是Idel1和Idel2的实例,而不是 Date实例。
结论
Dunamis 项目介绍了动态委托模式来扩展Java代理(Proxy)反射机制。它能够在运行时生成类和接口的委托。本文使用简单的例子简短地介绍了动态委托。在实际 里,许多领域能够使用动态委托,例如单元测试中的mock对象,Java GUI MVC框架,以及其他方面。
资源
Dunamis 项目: dunamis.dev.java.net
下载本文例子程序:例子代码
BCEL: jakarta.apache.org/bcel/index.html