java动态代理简单使用(Java动态代理机制和实现原理详解)

java动态代理简单使用(Java动态代理机制和实现原理详解)(1)

代理模式
  • Java动态代理运用了设计模式中常用的代理模式
  • 代理模式:目的就是为其他对象提供一个代理用来控制对某个真实对象的访问
  • 代理类的作用:为委托类预处理消息过滤消息并转发消息进行消息被委托类执行后的后续处理 通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时又可以实现自定义的控制策略,比如Spring中的AOP机制,这样使得在设计上获得更大的灵活性
  • 代理的基本构成:
  • 代理模式中有 Subject 角色 ,RealSubject 角色和 Proxy 角色:Subject: 负责定义 RealSubjectProxy 角色应该实现的接口RealSubject: 用来真正完成业务服务功能Proxy: 负责将自身的 Request 请求,调用 RealSubject 对应的 request 功能实现业务功能,自身不做真正的业务
  • 静态代理模式:当在代码阶段规定这种代理关系 ,Proxy 类通过编译器编译成 class 文件,当系统运行时,此 class 已经存在这种静态代理模式可以访问无法访问的资源,增强现有的接口业务功能方面有很大的优点.但是大量地使用这种静态代理,会使系统内的类规模增大,并且不易维护由于 ProxyRealSubject 的功能本质上是相同的 ,Proxy 只是中介的作用,这种代理在系统中的存在,会造成代码冗余
  • 为了解决静态代理模式的问题,就有了动态创建Proxy:在运行状态中,需要代理的地方,根据 SubjectRealSubject, 动态地创建一个 ProxyProxy使用完之后,就会销毁,这样就可以避免Proxy角色的class在系统中的冗余问题
Java动态代理
  • java.lang.reflect.Proxy:Java动态代理机制的主类提供一组静态方法为一组接口动态的生成对象和代理类

// 该方法用于获取指定代理对象所关联的调用处理器 public static InvocationHandler getInvocationHandler(Object proxy); // 该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象 public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces); // 该方法用于判断指定类对象是否是一个动态代理类 public static boolean isProxyClass(Class<?> cl); // 该方法用于为指定类装载器,一组接口以及调用处理器生成动态代理类实例 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

  • java.lang.reflect.Invocationhandler:调用处理器接口,自定义 invoke 方法用于实现对真正委托类的代理访问

/** * 该方法负责集中处理动态代理类上的所有方法调用 * * @param proxy 代理实例 * @param method 被调用的方法对象 * @param args 调用参数 * @return 返回调用处理器根据三个参数进行预处理或者分派到委托类实例上反射执行的对象 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

  • java.lang.ClassLoader:类装载器将类的字节码装载到Java虚拟机即JVM中,并为其定义类对象,然后该类才能被使用Proxy类与普通类的唯一区别就是 :Proxy类字节码是由JVM在运行时动态生成的而不是预存在于任何一个.calss文件中每次生成动态代理类对象时都需要指定一个类装载器对象
Java动态代理机制Java动态代理创建对象的过程:
  • 通过实现 InvocationHandler 接口创建自己的调用处理器

/* * InvocationHandlerImpl实现了InvocationHandler接口 * 并能实现方法调用从代理类到委托类的分派转发向委托类实例的引用,用于真正执行分派转发过来的方法调用 */ InvocationHandler handler = new InvocationHandlerImpl(...);

  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类

// 通过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 });

为了简化对象创建过程 ,Proxy 类中使用 newInstanceProxy 封装了 步骤2-步骤4, 因此只需要两个步骤即可完成代理对象的创建

// InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(...); // 通过 Proxy 直接创建动态代理类的实例 Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler);

Java动态代理注意点
  • 包:代理接口是 public, 则代理类被定义在顶层包 ,package 为空,否则 default, 代理类被定义在该接口所在的包

/* * 记录非公共代理接口的包,以便在同一个包中定义代理类 * 验证所有非公共代理接口是否都在同一个包中 */ for (int i =0; i < interfaces.length; i ) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf("."); String pkg = ((n == -1) ? "" : name.subString(0, n 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException("non-public interfaces from different packaes"); } } } if (proxyPkg == null) { // 没有使用非公共代理接口代理类的包 proxyPkg = ReflectUtil.PROXY_PACKAGE "."; }

  • 生成的代理类为public final,不能被继承
  • 类名的格式为 :"$ProxyN"N 是逐一递增的数字,代表Proxy是被第N次动态代理生成的代理类对于同一组接口,接口的排列顺序也相同,不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象,以此提高效率

synchronized (cache) { /* * 不必担心获取到清除掉弱引用的缓存 * 因为如果一个代理类已经被垃圾回收,代理类的类加载器也会被垃圾回收 * 所以获取到的缓存都是加载到缓存中的映射 */ do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); if (proxyClass != null) { /* * 代理类已经生成,返回代理类 */ return proxyClass; } else if (value == pendingGenerationmarker) { /* * 代理类正在生成,等待代理类生成 */ try { cache.wait(); } catch (InterruptedException e) { /* * 等待生成的代理类有一个极小的限定的时间,因此可以忽略线程在这里的影响 */ } continue; } else { /* * 如果没有这个接口列表已经生成或者正在生成的代理类 * 需要去生成这些接口的代理类,将这些接口标记为待生成 */ cache.put(key, pendingGenerationMarker); break; } }while (true); }

  • 类继承关系: Proxy 类是父类,这个规则适用于所有由 Proxy 创建的动态代理类(这也导致Java动态代理的缺陷,由于Java不支持多继承,所以无法实现对 class 的动态代理,只能对于 Interface 进行代理),该类实现了所有代理的一组接口,所以 Proxy 类能够被安全的类型转换到其所代理的某接口
  • 代理类的根类 java.lang.Object 中的 hashCode(),equals()和().toString 方法同样会被分派到调用处理器 invoke 方法执行
Java动态代理测试创建一个动态代理类

public class serviceProxy implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理对象 * @param target 真实对象 * @return 代理对象 */ public Object bind(Object target, Class[] interfaces) { this.target = target; // 取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /** * 通过代理对象调用方法首先进入这个方法 * @param proxy 代理对象 * @param Method 方法,被调用方法 * @param args 方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* * JDK动态代理 */ Object result = null; // 反射方法前调用 System.err.println("--反射方法前执行的方法--"); // 反射执行方法,相当于调用target.xXX() result = method.invoke(target, args); // 反射方法后调用 System.err.println("--反射方法后执行的方法--"); return result; } }

  • bind方法:bind方法中的 newProxyInstance 方法,就是生成一个代理对象第一个参数: 类加载器第二个参数: 真实委托对象所实现的接口. 代理对象挂在那个接口下第三个参数: this 代表当前 HelloServiceProxy 类, 也就是使用HelloServiceProxy作为对象的代理
  • invoke方法:invoke 方法有三个参数:第一个 proxy 是代理对象第二个是当前调用那个方法第三个是方法的参数
ProxyTest

public class ProxyTest { public static void main(String[] args) { HelloServiceProxy proxy = new HelloServiceProxy(); HelloService service = new HelloServiceImpl(); // 绑定代理对象 service = (HelloService) proxy.bind(service, new Class[] {HelloService.class}); service.sayHello("user"); } }

class文件分析
  • Java 编译器编译好 Java 文件后,产生 .class 文件在磁盘中:class 文件是二进制文件,内容是只有 JVM 虚拟机能够识别的机器码JVM 虚拟机读取字节码文件,取出二进制数据加载到内存中,解析 .class 文件内的信息,生成对应的 Class 对象
  • 加载 class 文件字节码到系统内,转换成 class 对象,然后再实例化:定义一个类自定义一个类加载器,用于将字节码转换成 class 对象编译 .class 文件,在程序中读取字节码,然后转换成相应的class对象,再实例化
在运行期生成二进制字节码
  • 在代码中,动态创建一个类:由于 JVM 通过字节码的二进制信息加载类,因此在运行期的系统中,遵循 Java 编译系统组织 .class 文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类
  • 可以使用开源框架在运行时期按照 Java 虚拟机规范对 class 文件的组织规则生成对应的二进制字节码. 比如 ASM,Javassist
ASM
  • ASM 是一个 Java 字节码操控框架:能够以二进制形式修改已有类或者动态生成类ASM 在创建 class 字节码的过程中,操纵的级别是底层 JVM 汇编指令级别ASM 可以直接产生二进制 class 文件,也可以在类被加载入Java虚拟机之前动态改变类行为ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户的要求生成新类
  • 通过ASM生成类的字节码:使用 ASM 框架提供的 ClassWriter 接口,通过访问者模式进行动态创建 class 字节码然后使用 Java 反编译工具 (JD_GUI) 打开硬盘上生成的 类.class 文件查看类信息再使用定义的类加载器将 class 文件加载到内存中,然后创建 class 对象,并且实例化一个对象,调用 code 方法,查看 code 方法中的结果
  • 至此表明: 在代码里生成字节码,并动态地加载成class对象,创建实例是完全可以实现的
Javassist
  • Javassist 是一个开源的分析,编辑和创建 Java 字节码的类库,已经加入 JBoss 应用服务器项目,通过使用 Javassist 对字节码操作为 JBoss 实现动态 AOP 框架:Javassist是JBoss一个子项目,主要优点在于简单快速直接使用Java编码的形式,不需要虚拟机指令,就能改变类的结构或者动态生成类
源码分析Proxy类

// 映射表: 用于维护类装载器对象到其对应的代理类缓存 private static Map loaderToCache = new WeakHashMap(); // 标记: 用于标记一个动态代理类正在被创建中 private static Object pendingGenerationMarker = new Object(); // 同步表: 记录已经被创建的动态代理类类型,主要通过方法isProxyClass进行相关判断 private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 关联的调用处理器引用 protected InvocationHandler h;

newProxyInstance
  • Proxy 静态方法 newProxyInstance:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { /* * 检查关联调用处理器是否为空,如果是空则抛出异常 */ if (h == null) { throw new NullPointerException(); } /* * 获得与指定类型装载器和一组接口相关的代理类类型对象 */ Class<?> cl = getProxyClass0(loader, interfaces); /* * 通过反射获取构造函数对象并生成代理类实例 */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { /* * 使用doPrivilege创建动态代理类实例 * 因为代理类实现可能需要特殊权限的非公共接口 */ return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } } private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { return cons.newInstance(new Object[] {h}); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantationException e) { throw new InternalException(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalException(e.toString()); } } }

  • 动态代理的真正的关键是在 getProxyClass0() 方法
getProxyClass0方法分析
  • 通过 getProxyClass0 方法中生成具体的 class 文件的过程:定义 pathclass 文件写到指定的硬盘中反编译生成的 class 文件

getProxyClass0()方法分为四个步骤:

  1. 对这组接口进行一定程度的安全检查:1.1 接口类对象是否对类装载器可见1.2 接口类对象与类装载器所识别的接口类对象完全相同1.3 确保是interface类型而不是class类型.

for (int i = 0; i < interfaces.length; i ) { /* * 验证类加载器是否将此接口的名称解析为相同的类对象 */ String interfaceName = interface[i].getName(); Class interfaceClass = null; try { /* * forName(String name, boolean initialize, ClassLoader loader) * Returns the Class object associated with the class or interface with the given string name, * using the given class loader */ interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interface[i]) { throw new IllegalArgumentException(interface[i] "is not visible from class loader."); } /* * 验证类加载器得到的类对象是否是interface类型 */ if (! interfaceClass.isInterface()) { throw new IllegalArgumentException(interfaceClass.getName() "is not an interface."); } /* * 验证类加载器得到的类对象接口不是一个重复的接口 */ if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException("repeated interface:" interface.getName()); } interfaceSet.add(interfaceClass); interfaceName[i] = interfaceName; }

  1. loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在,就会创建一个新的缓存表并更新到 loaderToCahe 中:2.1 loaderToCache 存放键值对 : 接口名字列表:动态生成的代理类的类对象的引用2.2 当代理类正在被创建时,会临时进行保存 : 接口名字列表:pendingGenerationMarker2.3 标记 pendingGenerationMarker 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成

/* * 寻找类加载器的缓存表,如果没有就为类加载器创建代理类缓存 */ Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache = put(loader, cache); } } do { /* * 以接口名字作为关键字获得对应的cache值 */ Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class)((Reference)value).get(); } if (proxyClass != null) { // 如果已经创建,直接返回 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在创建,保持等待 try { cache.wait() } catch (InterruptException e) { } // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待 continue; } else { // 标记代理类正在被创建 cache.put(key, pendingGenerationMarker); // 跳出循环已进入创建过程 break; } } while(true)

  1. 动态创建代理类的class对象

/* * 选择一个名字代理类来生成 */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber ; } String proxyName = proxyPkg proxyClassNamePrefix num; /* * 验证类加载器中没有使用这个名字定义的类 */ ... // 动态生成代理类的字节码数组 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); try { // 动态地定义新生成的代理类 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * 这里的类格式错误指的是生代理类代码中的错误 * 还有一些应用到代理类生成的参数的错误,比如一些虚拟机限制的超量 */ throw new IllegalArgumentException(e.toString()); } // 将生成的代理类对象记录到proxyClasses中 proxyClasses.put(proxyClass, null);

首先根据接口 public 与否的规则生成代理类的名称 - $ProxyN 格式,然后动态生成代理类. 所有的代码生成工作由 ProxyGenerator 完成,该类在 rt.jar 中,需要进行反编译

public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); // 动态生成代理类的字节码 final byte[] classFile = gen.generateClassFile(); // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try{ FileOutputStream file = new FileOutputStream(doToSlash(name) ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError("I/O exception saving generated file :" e); } } } ); } // 返回代理类的字节码 return classFile; }

  1. 代码生成过程进入结尾部分,根据结果更新缓存表. 如果代理类成功生成则将代理类的类对象引用更新进缓存表,否则清除缓存表中对应的关键值,最后唤醒所有可能的正在等待的线程

finally { synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass;

InvocationHandler解析
  • Proxy 角色在执行代理业务的时候,就是在调用真正业务之前或者之后完成一些额外的功能
  • 代理类就是在调用真实角色的方法之前或者之后做一些额外的业务
  • 为了构造具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发管理器,让这个管理器统一管理触发,这个触发管理器就是 InvocationHandler
  • 动态代理工作的基本工作模式:将方法功能的实现交给 InvocationHandler 角色外接对 Proxy 角色中每一个的调用 ,Proxy 角色都会交给 InvocationHandler 来处理InvocationHandle r则调用具体对象角色的方法
  • 在这种模式中,代理 ProxyRealSubject 应该实现相同的类的 public 方法,有两种方式:一个比较直观的方式: 就是定义一个功能接口,然后让 ProxyRealSubject 来实现这个接口 ( JDK 中的动态代理机制 - Java动态代理机制 )比较隐晦的方式: 通过继承实现 Proxy 继承 RealSubject. 因为Proxy继承自RealSubject, 这样Proxy则拥有了RealSubject的功能 ,Proxy 还可以通过重写 RealSubject 中的方法来实现多态( cglib )
JDK动态代理机制
  • JDK动态代理机制通过接口为RealSubject创建一个动态代理对象:获取 RealSubject 上的所有接口列表确定要生成的代理类类名根据需要实现的接口信息,在代码中动态创建该 Proxy 类的字节码将对应的字节码转换成对应的 class 对象创建 InvocationHandler, 用来处理 Proxy 所有方法调用Proxyclass 对象,以创建的 handler 为参数,实例化一个 proxy
  • JDK动态代理实例:定义两个接口Vehicle和RechargeableVehicle接口表示交通工具类,有drive()方法Rechargeable接口表示可充电,有recharge()方法定义一个实现两个接口的类ElectricCar,类图如下:
  • 创建ElectricCar的动态代理类:

/** * 交通工具接口 */ public interface Vehicle { public void drive(); }

/** * 充电接口 */ public interface Rechargable { public void recharge(); }

/** * 电动车类 * 实现Rechargable, Vehicle接口 */ public class ElectricCar implements Rechargable, Vehicle { @Override public void drive() { System.out.println("ElectricCar can drive."); } @Override public void recharge() { System.out.println("ElectricCar can recharge."); } }

/** * 动态代理类的触发器 */ public class InvocationHandlerImpl implements InvocationHandler { private ElectricCar car; public InvocationHandlerImpl(Electric car) { this.car = car; } @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { System.out.println("正在调用方法:" paramMethod.getName() "..."); paramMethod.invoke(car, null); System.out.println("方法" paramMethod.getName() "调用结束."); return null; } }

public class ProxyTest { public static void main(String[] args) { ElectricCar car = new ElectricCar(); // 获取对应的ClassLoader ClassLoader classLoader = car.getClass().getClassLoader(); // 获取ElectricCar所实现的所有接口 Class[] interfaces = car.getClass().getInterfaces(); // 设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用 InvocationHandler handler = new InvocationHandlerImpl(car); /* * 创建代理对象在这个过程中: * a. JDK根据传入的参数信息动态地在内存中创建和 .class 等同的字节码 * b. 根据相应的字节码转换成对应的class * c. 然后调用newInstance()创建实例 */ Object o = Proxy.newProxyInstance(classLoader, interfaces, handler); Vehicle vehicle = (Vehicle) o; vehicle.drive(); Rechargable rechargable = (Rechargable) o; rechargable.recharge(); } }

  • 生成动态代理类的字节码并且保存到硬盘中:
  • JDK提供了 sun.misc.ProxyGenerator.generateProxyClass(String proxyName, calss[] interfaces) 底层方法来产生动态代理类的字节码
  • 定义一个工具类,用来将生成的动态代理类保存到硬盘中:

public class proxyUtils { /* * 根据类信息,动态生成二进制字节码保存到硬盘中 * 默认是clazz目录下 * * @params clazz 需要生成动态代理类的类 * @proxyName 动态生成代理类的名称 */ public static void generateClassFile(Class clazz, String proxyName) { // 根据提供的代理类名称和类信息,生成字节码 byte[] classFile = ProxyGenerator.generateProxyClass(ProxyName, clazz.getInterfaces()); String paths = clazz.getResource(".").getPath(); System.out.println(paths); FileOutputStream out = null; try { // 保留到硬盘中 out = new FileOutputStream(paths proxyName ".class"); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }

  • 修改代理类名称为 "ElectricCarProxy", 并保存到硬盘,使用以下语句:

ProxyUtils.generateClassFile(car.getClass(), "ElectricCarProxy");

这样将在 ElectricCar.class 同级目录下产生 ElectricCarProxy.class 文件

  • 使用反编译工具 jd-gui.exe 打开,将会看到以下信息:

/** * 生成的动态代理类的组织模式是继承Proxy类,然后实现需要实现代理的类上的所有接口 * 在实现过程中,是通过将所有的方法都交给InvocationHandler来处理 */ public final class ElectricCarProxy extends Proxy implements Rechargable,Vehicle { private static Method m1; private static Method m3; private static Method m4; private static Method m0; private static Method m2; public ElectricCarProxy(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { /* * 方法功能的实现交给InvocationHandler处理 */ return ((Boolean) this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final void recharge() throws { try { /* * 方法功能的实现交给InvocationHandler处理 */ this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final drive() throws { try { /* * 方法实现交给InvocationHandler处理 */ this.h.invoke(this, m4, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final int hasCode() throws { try { /* * 方法功能交给InvocationHandler处理 */ return ((Integer) this.h.invoke(this, m0, null)).intValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final String toString() throws { try { /* * 方法功能实现交给InvocationHandler处理 */ return (String)this.h.invoke(this, m2, null); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } static { try { /* * 为每一个需要方法对象 * 当调用相应的方法时,分别将方法对象作为参数传递给InvocationHandler处理 */ m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.oxford.proxy.Rechargable").getMethod("recharge", new Class[0]); m4 = Class.forName("com.oxford.proxy.Vehicle").getMethod("drive", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hasCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessge()); } } }

  • 生成的动态代理类的特点:继承自 java.lang.reflect.Proxy, 实现了 Rechargable,Vehicle 这两个 ElectricCar 接口类中的所有方法都是 final 的所有的方法功能的实现都统一调用了 InvocationHandlerinvoke() 方法
CGLIB动态代理机制
  • CGLIB 通过类继承生成动态代理类
  • JDK动态代理类的特点:
    • 某个类必须有实现的接口,而生成的代理类只能代理某个类接口定义的方法. 这样会导致子类实现继承两个接口的方法外,另外实现的方法,在产生的动态代理类中不会有这个方法
    • 如果某个类没有实现接口,那么这个类就不能使用JDK动态代理了
  • CGLIB: Code Generation Library, CGLIB 是一个强大的,高性能,高质量的 Code 生成类库,可以在运行时期扩展 Java 类与实现 Java 接口
  • CGLIB创建类的动态代理类的模式:查找类中所有非 finalpublic 类型的方法定义将这些方法的定义转换成字节码将组成的字节码转换成相应的代理的 class 对象实现 MethodInterceptor 接口,用来处理对代理类上的所有方法的请求 (类似JDK动态代理中的InvocationHandler的作用)
  • 定义一个 Programmer 类,一个 Hacker

/** * 开发人员类 */ public class Programmer { public void code() { System.out.println("开发人员开发程序代码."); } }

/** * 黑客类 * 实现了方法拦截器接口 */ public class Hacker implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Hacker what to do."); proxy.invokeSuper(obj, args); System.out.println("Hacker done this."); return null; } }

  • 测试类:

public class Test { public static void main(String[] args) { Programmer programmer = new Programmer(); Hacker hacker = new Hacker(); // CGLIB中的加强器,用来创建动态代理 Enhancer enhancer = new Enhancer(); // 设置要创建动态代理的类 enhancer.setSuperclass(programmer.getClass()); /* * 设置回调 * 这里相当于对于代理类上所有方法的调用,都会调用CallBack * 而CallBack则需要实行intercept()方法进行拦截 */ enhancer.setCallBack(hacker); Programmer proxy = (Programmer) enhancer.create(); proxy.code(); } }

  • 通过 CGLIB 生成的 class 文件的内容:

public class Programmer EnhancerByCGLIB fa7aa2cd extends Programmer implements Factory { /* * .... */ // Enhancer传入的methodInterceptor private MethodInterceptor CGLIB$CALLBACK_0; /* * ... */ public final void code() { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { tmp4_1; // 若callback不为空,则调用methodInterceptor的intercept()方法 CGLIB$BIND_CALLBACKS(this); } if (this.CGLIB$CALLBACK_0 != null) return; // 如果没有设置callback回调函数,则默认执行父类的方法 super.code(); } /* * ... */ }

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页