|
真爱的事业和真正的爱情一生只有一次,都值得我们温柔地相待,因为那种感觉是永远都无法复制的, 这世界真正属于你的东西其实并不多,你不好好珍惜,它便会离你而去,包括机遇,包括爱情,包括生命。 不要找任何理由, 当幸福在你身边的时候就抓住它,你就一定会很幸福! |
时 间 记 忆 |
« | September 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | | | | |
|
blog名称:玻璃杯中的花生壳 日志总数:162 评论数量:249 留言数量:1 访问次数:828299 建立时间:2004年11月4日 |
 | | |
|
|
代理模式是一个在面向对象编程中重要而广泛被使用的设计模式。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展现了这种结构:500)this.width=500'> 图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所示:500)this.width=500'> 图2. TestBeanDelegation的类图TestBeanDelegation
实现了Delegation接口并且继承了TestBean类。它也包含了对TestBean和DelegationInvocationHandler
的引用。所有TestBeanDelegation上的方法调用都是对这两者的委托。以getName()为例,图3描述了方法调用的顺序图。500)this.width=500'>图3. TestBeanDelegation.getName()调用的顺序图相关伪码如下://The delegation class is a sub-class of the class to be delegatedpublic 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。创建一个具体类的委托类假定存在一个名字为ConcreteClass的具体类://ConcreteClass.javapackage 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.javapackage 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_0trueDelegationGenerator.getDelegationClass()接受类数组为参数,返回一个Java类,这个类继承了给定类或者实现了给定接口。缺省情况下,生成的委托类和被委托类是在同一个包中。委托类能够像下面那样被实例化://object to be delegatedObject obj = ...; //some concrete invocation handler instanceDelegationInvocationHandler h = ...; Constructor c = clazz.getConstructor(new Class[] { Object.class });Object inst = c.newInstance(new Object[] {obj});((Delegation) inst)._setInvocationHandler(h);练习2:创建一个抽象类的委托类DelegationGenerator也能够生成一个对于抽象类的具体的委托类//AbstractClass.javapackage org.jingle.util.dydelegation.sample;public abstract class AbstractClass { public abstract void wave();}//AbstractClassTest.javapackage 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_0false生成的委托类是具体类而不是抽象类。练习3。创建类和多个接口的委托类DelegationGenerator.getDelegationClass()能够同时委托一个类和多个接口,生成委托类来委托给定的类和接口。并且,将去除重复的接口。//Idel1.javapackage org.jingle.util.dydelegation.sample.bean;public interface Idel1 { public void idel1();}//Idel2.javapackage org.jingle.util.dydelegation.sample.bean;public interface Idel2 { public void idel2();}//ComplexClassTest.javapackage 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)); }}输出:truetruetrue生成的委托类扩展了给定的类ConcreteClass,实现了所有的给定接口:Idel1和Idel2。练习4。创建单个对象的委托对象根据一个特定的被委托对象,DelegationGenerator能够直接生成一个委托对象。// ConcreteClassTest2.javapackage 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 SimpleHandlerHello from ConcreteClassHello again from ConcreteClassInterrupted by SimpleHandlerorg.jingle.util.dydelegation.sample.ConcreteClass@ef5502DummyInvocationHandler
是一个DelegationInvocationHandler虚拟的实现。它总是在方法invokeBefore()中返回true,在方法
invokeAfter()中直接返回输入的值,并且在invokeAfterException()中直接抛出输入异常throwable。带有
DummyInvocationHandler委托对象和被委托对象有相同的动作。DelegationGenerator.newDelegationInstance()将一个对象和DelegationInvocationHandler实例作为参数。它返回委托对象来委托给定对象。所有对委托对象的调用方法将被DelegationInvocationHandler实例委托,除了下面的方法:没有public限定符的方法。没有final限定符的方法有static限定符的方法定义在一个对象类中,除了hashCode(),equals()和toString()而外的方法。练习5。创建一个Java核心类对象的委托对象你曾经想过委托存在的Java核心类对象吗?可以像通常所做的那样委托它。//DateTest.javapackage 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_0True date = 1099380377665Delegation date = 1099380376665当创建一个Java核心类的委托类时,委托类不会和Java核心类在同一个包中,因为Java安全模型不允许用户定义的ClassLoader定义以java开头的包中的类。DateClassHandler代理在invokeAfter()中的getTime()方法调用,返回的值比正常返回值低1000。高级用法练习6。模拟代理行为委托能够做代理所做的事吗?绝对可以!动态委托覆盖了代理的功能。给定一个适当的委托句柄,它能够模拟Java代理的行为。// ProxyTest.javapackage 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 idel1Hello from idel2ProxyHandler在方法invokeBefore()中返回false,这意味着在委托对象上的所有方法调用都不会代理原来的对象。与代理Proxy相同,它使用方法invokeAfter()来定义委托行为。DelegationGenerator.newDelegationInstance()方法有另一个版本。它包含4个参数:被委托的对象这可能是null。如果它不是null,它必须是所有给定的类和接口的实例被委托的类数组这可能包含多个接口,和一个类。委托类名字如果是null,系统将会生成一个名字一个DelegationInvocationHandler实例,使用它来定义委托的行为。从输出结果,我们可以看到委托对象是两个接口Idel1和Idel2的实例。它的行为就是定义在句柄中的。练习7.部分委托直到现在,我们已经委托了指定对象所有的函数。怎么样来进行对象函数的部分委托呢?//MyDate.javapackage 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.javapackage 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); }}输出truetruefalseMyDate
扩展了Date类,实现了Idel1和Idel2接口。DelegationGenerator.newDelegationInstance()使用
MyDate实例作为被委托的实例,将委托范围限制在Idel1和Idel2中。换句话说,生成的委托对象是Idel1和Idel2的实例,而不是
Date实例。结论Dunamis
项目介绍了动态委托模式来扩展Java代理(Proxy)反射机制。它能够在运行时生成类和接口的委托。本文使用简单的例子简短地介绍了动态委托。在实际
里,许多领域能够使用动态委托,例如单元测试中的mock对象,Java GUI MVC框架,以及其他方面。 |
|
| | |
|