- 动态代理的实现
- 动态代理的应用
- JDK Proxy 和 CGLib 的区别:
- JDK Proxy 动态代理实现
- CGLib 的实现
- 总结
动态代理是程序在 运行期间 动态构建代理对象 和 动态调用代理方法 的一种机制。
- 动态代理的常用实现方式是 ① 反射;② 也可以通过 CGLib 来实现。
反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。
CGLib 是基于 ASM(一个 Java 字节码 *** 作框架)而非反射实现的。
动态代理的应用ASM是一个Java字节码 *** 纵框架,它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。Java class被存储在严格格式定义的.class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类
-
动态代理的常见使用场景有 RPC 框架的封装、AOP(面向切面编程)的实现、JDBC 的连接等。
-
Spring 框架中同时使用了两种动态代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会使用 JDK Proxy,在没有实现接口时就会使用 CGLib,我们也可以在配置中指定强制使用 CGLib,只需要在 Spring 配置中添加 即可。
- JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
- Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
- JDK Proxy 是通过拦截器加反射的方式实现的;
- JDK Proxy 只能代理继承接口的类;
- JDK Proxy 实现和调用起来比较简单;
- CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
- CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。
动态代理中有一个重要的角色也就是代理器,它用于统一管理被代理的对象。
JDK Proxy 动态代理中,InvocationHandler 就是这个代理器,而 invoke() 方法则是触发代理的执行方法。 无需引用第三方类,只需要实InvocationHandler 接口,重写 invoke() 方法。
InvocationHandler 源码
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
InvocationHandler 的实现
public class ProxyExample { static interface Car { void running(); } static class Bus implements Car { @Override public void running() { System.out.println("The bus is running."); } } static class Taxi implements Car { @Override public void running() { System.out.println("The taxi is running."); } } }
static class JDKProxy implements InvocationHandler { private Object target; // 代理对象 // 获取到代理对象 public Object getInstance(Object target) { this.target = target; // 取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { // 执行调用方法(此方法调用前后,可以进行相关业务处理) Object result = method.invoke(target, args); return result; } public static void main(String[] args) { // 执行 JDK Proxy JDKProxy jdkProxy = new JDKProxy(); Car carInstance = (Car) jdkProxy.getInstance(new Taxi()); carInstance.running(); } }CGLib 的实现
在使用 CGLib 之前,我们要先在项目中引入 CGLib 框架,在 pom.xml 中添加如下配置
cglib cglib3.3.0
CGLib 实现代码如下:
public class CGLibExample { static class Car { public void running() { System.out.println("The car is running."); } } static class CGLibProxy implements MethodInterceptor { private Object target; // 代理对象 public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); // 设置父类为实例类 enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("方法调用前业务处理."); Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用 return result; } } // 执行 CGLib 的方法调用 public static void main(String[] args) { // 创建 CGLib 代理类 CGLibProxy proxy = new CGLibProxy(); // 初始化代理对象 Car car = (Car) proxy.getInstance(new Car()); // 执行方法 car.running();总结
可以看出 CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。JDK Proxy 只能代理继承接口的类;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)