动态代理两种方式
1.动态代理两种方式简述
JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
2.动态代理的特点
1.字节码随用随创建,随用随加载。
2.它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
3.装饰者模式就是静态代理的一种体现。
3.动态代理常用的有两种方式
3.1 基于接口的动态代理
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。
3.2 基于子类的动态代理
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。
4.使用JDK官方的Porxy类创建对象
实体类:
package com.jh.spring13jdk动态代理; import lombok.Data; @Data public class Game implements Open{ //游戏的网速 private int ms = 460; @Override public int openApp() { System.out.println("打开游戏后的网速是:" + this.getMs()); return this.getMs(); } }
接口:
package com.jh.spring13jdk动态代理; public interface Open { int openApp(); }
测试类:
package com.jh.spring13jdk动态代理; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Spring13Test { //JDK动态代理 省略了代理对象!!! 直接有jdk中的代理类来实现,但是目标类必须有接口 @Test public void test01() { final Game game = new Game(); /** * 代理: * 间接。 * 获取代理对象: * 要求: * 被代理类最少实现一个接口 * 创建的方式 * Proxy.newProxyInstance(三个参数) * 参数含义: * ClassLoader:和被代理对象使用相同的类加载器。 * Interfaces:和被代理对象具有相同的行为。实现相同的接口。 * InvocationHandler:如何代理。 * 策略模式:使用场景是: * 数据有了,目的明确。 * 如何达成目标,就是策略。 * */ //使用JDK动态类对象,当作迅游加速器的类,代替了静态的代理类 //Proxy:代理的意思。 newProxyInstancece创建代理对象 Open jdkProxy = (Open) Proxy.newProxyInstance( game.getClass().getClassLoader(), //类的加载器 game.getClass().getInterfaces(), //类的所有接口 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Integer ms = (Integer) method.invoke(game, args); if (ms != null) { ms = ms - 400; } return ms; } } ); int i = jdkProxy.openApp(); System.out.println("i=" + i); } }
5.使用CGLib的Enhancer类创建代理对象
对于没有接口的类,如何实现动态代理呢,这就需要CGLib了,CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承。所以不能对final修饰的类进行代理。
实体类:
package com.jh.spring14cjlib动态代理; import lombok.Data; /** * 目标类 * 父母 */ @Data public class Parents { //成绩 private int score = 599; //高考 public int gaoKao(){ System.out.println("父母参加高考,分数是:"+this.getScore()); return this.getScore(); } }
工厂类:
package com.jh.spring14cjlib动态代理; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; //MethodInterceptor:方法上的拦截 public class ParentsFactory implements MethodInterceptor { private Parents parents; public ParentsFactory() { parents = new Parents(); } /** * 基于子类的动态代理 * 要求: * 被代理对象不能是最终类 * 用到的类: * Enhancer * 用到的方法: * create(Class, Callback) * 方法的参数: * Class:被代理对象的字节码 * Callback:如何代理 */ //增强器 ,把parents创造一个子类 public Parents createParentsSon() { //使用字节码增强器,去增强我们的父类 Enhancer enhancer = new Enhancer(); //字节码增强器,可以读懂class文件 //enhancer 指定一个对象 enhancer.setSuperclass(Parents.class);//反射 //使用工厂,换行(创建子类) enhancer.setCallback(this); //创建子类 Parents son = (Parents) enhancer.create(); //多态 return son; } /** * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何 方法进行增强。 * * 参数: * 前三个和基于接口的动态代理是一样的。 * MethodProxy:当前执行方法的代理对象。 * 返回值: * 当前执行方法的返回值 */ //方法的拦截 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Integer score = (Integer) method.invoke(parents, objects); if (score != null) { score = score + 30; } return score; } }
测试类:
package com.jh.spring14cjlib动态代理; import org.junit.Test; public class Spring14Test { @Test public void test01(){ ParentsFactory parentsFactory = new ParentsFactory(); Parents parentsSon = parentsFactory.createParentsSon(); int score = parentsSon.getScore(); System.out.println(score); } }
6.总结:
CGLib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLib创建对象时所花费的时间却比JDK多的的。
7.问题:
为什么,要在不改变源代码的基础上,去写一个代理类增强一些功能呢?
因为项目大了,就有主要的功能和次要的功能,要想主要功能和次要功能一起运行,必须用AOP
我再解释下面向对象和面向切面,面向对象OOP,面向的是主要功能的对象,而AOP是面向对象OOP的一个补充,面向次要功能的对象
目的是为了降低耦合度,提高代码的复用性。(自己总结的,仅供参考,你懂的,嘻嘻)。