黑马程序员java高新技术篇----动态代理

来源:互联网 发布:软件说明文档 编辑:程序博客网 时间:2024/05/21 19:46

android培训java培训期待与您交流!

一、动态代理概述
1、Proxy提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。 动态代理类是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口是代理类实现的一个接口。代理实例是代理类的一个实例。每个代理实例都有一个关联的调用处理程序对象,它可以实现接口InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的Invoke方法,并传递代理实例、识别调用方法的java.lang.reflect.Method对象以及包含参数的Object类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

2、代理类具用以下属性:

(1)、代理类是公共的、最终的,而不是抽象的。
(2)、未指定代理类的非限定名称。但是,以字符串"$Proxy"开头的类名空间应该为代理类保留。
(3)、代理类扩展java.lang.reflect.Proxy。
(4)、代理类会按同一顺序准确地实现其创建时指定的接口。
(5)、如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
(6)、由于代理类将实现所有在其创建时指定的接口,所以对其Class对象调用getInterfaces将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其Class对象调用getMethods将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用getMethod将会在代理接口中找到期望的一些方法。
(7)、如果Proxy.isProxyClass方法传递代理类(由Proxy.getProxyClass返回的类,或由Proxy.newProxyInstance返回的对象的类),则该方法返回true,否则返回false。
(8)、代理类的java.security.ProtectionDomain与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予java.security.AllPermission。
(9)、每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射API才能访问公共构造方法,通过调用Proxy.newInstance 方法(将调用Proxy.getProxyClass的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

3、代理实例具有以下属性:

(1)、提供代理实例proxy和一个由其代理类Foo实现的接口,以下表达式将返回true:
                    proxy instanceof Foo
     并且以下的强制转换操作将会成功(而不抛出ClassCastException):
                   (Foo) proxy
(2)、每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态Proxy.getInvocationHandler方法将返回与作为其参数传递的代理实例相关的调用处理程序。
(3)、代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的 Invoke 方法。
(4)、在代理实例上的java.lang.Object中声明的hashCode、equals或toString方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的invoke方法,如上所述。传递到invoke的Method对象的声明类是java.lang.Object。代理类不重写从java.lang.Object继承的代理实例的其他公共方法,所以这些方法的调用行为与其对java.lang.Object实例的操作一样。

4、在多代理接口中重复的方法

当代理类的两个或多个接口包含一个具有相同名称和参数签名的方法时,代理类的接口顺序变得非常重要。在代理实例上调用重复方法 时,传递到调用处理程序的Method对象没有必要成为其声明类可以从接口(通过该接口调用代理方法)的引用类型指派的对象。此限制存在的原因是,生成的代理类中的相应方法实现无法确定它通过哪一个接口调用。因此,在代理实例上调用重复方法时,第一个接口中的方法的Method对象包含接口的代理类列表中的方法(直接或通过超级接口继承),该对象会传递到调用处理程序的invoke方法,无论该方法调用通过哪一种引用类型发生。 如果代理接口包含某一方法,它的名称和参数签名与java.lang.Object的hashCode、equals或toString方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method对象将使java.lang.Object成为其声明类。换句话说,java.lang.Object公共的非最终方法理论上在所有代理接口之前,以便确定哪一个 Method 对象传递到调用处理程序。 还要注意,当重复方法被指派到调用处理程序时,invoke方法只可以抛出经过检查的异常类型,该异常类型可以使用所有代理接口(可以通过它调用)中方法的throws子句指派一种异常类型。如果invoke方法抛出一个经过检查的异常,该异常没有指派给任何由一个理接口(可以通过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的UndeclaredThrowableException。此限制表示并非所有的由传递到invoke方法的Method对象上调用getExceptionTypes 返回的异常类型都可以由invoke方法成功抛出。

二、动态代理的创建方式

1、getProxyClasspublic static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。该代理类将由指定的类加载器定义,并将实现提供的所有接口。如果类加载器已经定义了具有相同排列接口的代理类,那么现有的代理类将被返回;否则,类加载器将动态生成并定义这些接口的代理类。对可以传递给 Proxy.getProxyClass 的参数有以下几个限制:
(1)、interfaces 数组中的所有 Class 对象必须表示接口,而不能表示类或基本类型。
(2)、interfaces 数组中的两个元素不能引用同一 Class 对象。
(3)、所有接口类型的名称通过特定的类加载器必须可见。换句话说,对于类加载器cl和所有接口i,以下表达式必须为true:
     Class.forName(i.getName(), false, cl) == i
(4)、所有非公共接口必须位于同一包中;否则,该代理类将不可能实现所有的接口,无论它在哪一个包中定义。
(5)、对于有相同签名的指定接口中任何成员方法集:
如果任何方法的返回类型是基本类型或 void,那么所有的方法必须具有与此相同的返回类型。
否则,该方法之一必须是返回类型,它可以指派给该方法其余的所有返回类型。
(6)、得到的代理类必须不超过虚拟机在类上施加的任何限制。例如,虚拟机可以限制某一类实现至65535的接口数;在这种情况下,interfaces数组的大小必须不超过65535。
(7)、指定的代理接口的顺序非常重要:对接口组合相同但顺序不同的代理类的两个请求会导致两个不同的代理类。

2、public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:

 Proxy.getProxyClass(loader, interfaces).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

三、通过动态代理的一个应用

要强的一点是:动态代理是为了增强程序的灵活性而不是单纯的简化程序,事实上有时候会让程序变得更复杂一些,但是相对于更改源码所带来的灾难和单纯的扩展所导致的臃肿,这种复杂性的性价比还是很高的。下面是一个图例,来说明这种灵活性的应用

四、动态代理的一个小程序的演示:

//该接口包含的方法就是要在代理类中动态插入的功能public interface AttachFunction {void beforMethod(Method method);void afterMethod(Method method);}
//面向接口编程:该类实现了AttachFunction接口//该类的实例将作为方法持有对象传入MyInvocationHandler构造器供invoke调用其中的功能//以实现在目标对象方法前后有多作为public class MyAttachFunction implements AttachFunction {@Overridepublic void beforMethod(Method method) {System.out.println("--------方法前--------");System.out.println(method.getName());}@Overridepublic void afterMethod(Method method) {System.out.println(method.getReturnType().getName());System.out.println("--------方法后--------");System.out.println();System.out.println();}}
public class MyInvocationHandler implements InvocationHandler {private Object target;  //目标对象:通常都是为指定的目标对象生成动态代理private AttachFunction amethod;public MyInvocationHandler(Object target,AttachFunction amethod){this.target = target;this.amethod = amethod;}@Override//proxy代表代理对象的    method代理对象所调用的方法  args代表代理对象所调用方法的参数public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {amethod.beforMethod(method);//在目标对象方法前增加功能Object resVal = method.invoke(target, args);//调用目标对象方法amethod.afterMethod(method);//在目标对象方法后增加功能return resVal; //返回目标对象值给代理对象,作为其所调用方法的返回值}}
//动态代理工厂,简单的工厂模式public class ProxyFactory {public static Object getProxy(Object target,AttachFunction amethod){return Proxy.newProxyInstance(target.getClass().getClassLoader(), //这里代理类实现了与目标对象相同的接口,所以代理对象可以作为目标对象使用target.getClass().getInterfaces(),new MyInvocationHandler(target,amethod));}}

下面就可以对任意目标对象的任意方法插入附加拱了,如果在invoke()方法里加入逻辑判断,还可以实现附加功能的选择性添加。
 

 

 

原创粉丝点击