本文共 5055 字,大约阅读时间需要 16 分钟。
一、动态代理理论
动态代理类是在程序运行期间,由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件(相当于jvm替
我们实现一个代理类)。代理类和委托类的关系是在程序运行时确定。
动态代理中三个重要的Java API,分别是:
java.lang.reflect.Proxy所有动态代理类的父类
java.lang.reflect.InvocationHandler调用处理器接口
java.lang.ClassLoader类装载器类
1、java.lang.reflect.Proxy
这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。Proxy类的静态方法,代码如下:
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器 static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象 static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:该方法用于判断指定类对象是否是一个动态代理类 static boolean isProxyClass(Class cl) // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
2、java.lang.reflect.InvocationHandler
这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
InvocationHandler的核心方法,代码如下:
/** * 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象 * 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行 */Object invoke(Object proxy, Method method, Object[] args)3、java.lang.ClassLoader 类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。 每次生成动态代理类对象时都需要指定一个类装载器对象。
二、动态代理实现步骤
具体步骤是: a. 实现InvocationHandler接口创建自己的调用处理器 ; b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 ; c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数 ; d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象;1、分步骤实现如下:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
2、封装:
Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
三、动态代理实现示例
接口Subject类和委托类RealSubject类的定义参见《》
1、创建自己的调用处理器
package com._test.proxy.dynamic;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import com._test.proxy.Subject;/** * 动态代理类对应的调用处理类 */public class SubjectInvocationHandler implements InvocationHandler { // 代理类持有一个委托类的对象应用 private Subject realSubjct; public SubjectInvocationHandler(Subject realSubject) { this.realSubjct = realSubject; } /** * 该方法负责集中处理动态代理类上的所有方法调用。 * 第一个参数是代理类实例, * 第二个参数是被调用的方法对象 ; * 第三个方法是调用参数。 * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理类接到这个任务:"+args[0].toString()); long stime = System.currentTimeMillis();// 委托类工作开始前时间 /** * 利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。 * 因为示例程序没有返回值,所以这里忽略了返回值处理 */ method.invoke(realSubjct, args) ; long endTime = System.currentTimeMillis();// 委托类工作结束后时间 System.out.println("执行任务总耗时" + (endTime - stime) + "毫秒"); return null; }}2、生成动态代理工厂
package com._test.proxy.dynamic;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import org.apache.pdfbox.pdmodel.graphics.predictor.Sub;import com._test.proxy.RealSubject;import com._test.proxy.Subject;/** * 生成动态代理对象的工厂 */public class DynamicProxyFactory { /** * 客户类调用此工厂方法获得代理对象 * * @return 对客户来说,并不知道返回的是代理类还是委托类 */ public static Subject getInstance() { // 创建一个真实类 Subject realSubject = new RealSubject(); /** * 创建动态代理类对应的调用处理类,将委托类传进去 */ InvocationHandler handler = new SubjectInvocationHandler(realSubject); /** * 通过 Proxy 直接创建动态代理类实例 * 1、第一个参数ClassLoader loader:指定类的装载器 ,如委托类的类装载器; * 2、第二个参数Class [] interfaces:一组接口的动态代理类的类对象 ,委托类的接口对象; * 3、第三个参数InvocationHandler h:指定代理对象所关联的调用处理器. */ Subject proxy = (Subject) Proxy.newProxyInstance(realSubject.getClass() .getClassLoader(), realSubject.getClass().getInterfaces(), handler); return proxy; }}3、动态代理客户类:
package com._test.proxy.dynamic;import com._test.proxy.Subject;public class Client { public static void main(String[] args) { Subject proxy = DynamicProxyFactory.getInstance() ; String taskName = "来加班"; System.out.println("客户端:公司要求员工" + taskName); proxy.doSomething(taskName); }}4、结果:
动态代理的优点和美中不足
优点: 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。 在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),实际中可以类似Spring AOP那样配置外围业务。美中不足:
Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾,那就是它仅支持 interface 代理。 因为它的设计注定就是面向接口的,回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。而Java 的继承机制注定了这些动态代理类们只能有一个继承类(因为已经继承了Proxy),所以无法实现对 class 的动态代理,原因是Java 中不可以多继承。参考博文地址:http://layznet.iteye.com/blog/1182924