静态代理和动态代理
一、什么是代理模式?
代理模式(Proxy Pattern)给某一个对象提供一个代理,并由代理对象控制原对象的引用。代理对象在客户端和目标对象之间起到中介作用。
代理模式可以分为静态代理和动态代理两种类型,而动态代理中又分为JDK动态代理和CGLIB代理两种。
如下图:
二、静态代理
静态代理的特点是, 为每一个业务增强都提供一个代理类, 由代理类来创建代理对象。
三、动态代理
静态代理会为每一个业务增强都提供一个代理类, 由代理类来创建代理对象, 而动态代理并不存在代理类, 代理对象直接由代理生成工具动态生成。
1. JDK动态代理
1)实现原理
在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。
Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。JDK动态代理是使用 java.lang.reflect 包下的代理类来实现. JDK动态代理必须要有接口
JDK的动态代理是基于反射实现。JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,回调我们实现的InvocationHandler接口的invoke方法。并且这个代理类是Proxy类的子类。这就是JDK动态代理大致的实现方式。
2)JDK动态代理步骤
3)代码示例
定义一个学生管理的接口:
1 public interface IStudentService { 2 3 void insertStudent(); 4 5 void deleteStudent(); 6 }
实现学生管理接口的类:
1 public class StudentService implements IStudentService { 2 public void insertStudent() { 3 //添加学生 4 System.out.println("添加学生"); 5 } 6 7 public void deleteStudent() { 8 //删除学生 9 System.out.println("删除学生"); 10 } 11 }
定义一个JDK动态代理类:
1 public class MyInvocationHandler implements InvocationHandler { 2 private Object obj; 3 4 public MyInvocationHandler(Object object){ 5 this.obj = object; 6 } 7 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 10 System.out.println(method.getName() + "方法调用前"); 11 Object result = method.invoke(obj, args); 12 System.out.println(method.getName() + "方法调用后"); 13 return result; 14 } 15 }
测试:
1 public class Client { 2 public static void main(String[] args) { 3 IStudentService studentService = new StudentService(); 4 5 // 创建 InvocationHandler 实现 6 InvocationHandler myInvocationHandler = new MyInvocationHandler(studentService); 7 8 // 通过 Proxy.newProxyInstance 动态生成代理对象 9 IStudentService studentServiceProxy = (IStudentService) Proxy.newProxyInstance( 10 studentService.getClass().getClassLoader(), 11 studentService.getClass().getInterfaces(), 12 myInvocationHandler); 13 studentServiceProxy.insertStudent(); 14 studentServiceProxy.deleteStudent(); 15 } 16 17 }
运行结果:
1 insertStudent方法调用前 2 添加学生 3 insertStudent方法调用后 4 deleteStudent方法调用前 5 删除学生 6 deleteStudent方法调用后
2. CGLIB动态代理
JDK动态代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时我们可以使用CGLIB动态代理. CGLIB动态代理的原理是生成目标类的子类, 这个子类对象就是代理对象, 代理对象是被增强过的.
注意: 不管有没有接口都可以使用CGLIB动态代理, 而不是只有在无接口的情况下才能使用。
四、JDK和CGLib动态代理对比
JDK 动态代理是实现了被代理对象所实现的接口,CGLib是继承了被代理对象。
JDK和CGLib 都是在运行期生成字节码,JDK是直接写Class字节码,CGLib 使用 ASM 框架写Class字节码,Cglib代理实现更复杂,生成代理类的效率比JDK代理低。
JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass机制直接调用方法,CGLib 执行效率更高。
1. 原理区别:
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3)如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
2. 性能区别:
1)CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。
3. 各自局限:
1)JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。
2)cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
3)由于CGLib实现代理方法的方式是重写父类的方法,所以无法对final方法,或者private方法进行代理,因为子类无法重写这些方法;
五、静态代理和动态的本质区别
1. 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2. 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3. 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
参考链接:
https://nilzzzz.github.io
https://juejin.cn/post/7011357346018361375
标签:调用,JDK,静态,代理,接口,CGLIB,动态 From: https://www.cnblogs.com/hld123/p/18210054