通俗易懂 快速理解 JDK动态代理 和 cglib动态代理
-
动态代理的实现方案有两种,JDK动态代理和CGLIB动态代理,区别在于JDK自带的动态代理,必须要有接口,而CGLIB动态代理有没有接口都可以。
-
JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
-
cglib动态代理:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。(CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。)
没有实现接口或者不需要实现接口的类,我们可以通过cglib动态代理对它进行代理。
基于cglib实现的动态代理需要引入第三方cglib库,之后我们可以实现基于子类的动态代理。
使用cglib实现的动态代理也有一个约束条件,就是被代理类不能被final修饰,
使用cglib实现的动态代理核心是Enhancer类,仔细观察我们会发现cglib动态代理的实现过程和JDK实现动态代理的过程极其类似。
提示:务必仔细看代码注释!!!注释上有很详细的解释
导航栏:
cglib动态代理
注意:这里需要引入依赖:cglib 2.1_3.jar
(如果不是maven项目,也可以手动导入cglib的jar包进行测试)
<dependency><groupId>cglibgroupId><artifactId>cglibartifactId><version>2.1_3version>dependency>
测试demo结构:
代码:
被代理类 Star:
package com.tong;public class Star { public void sing() { System.out.println("唱......."); } public void dance() { System.out.println("跳......"); }}
被代理类 ChineseCartoon:
package com.tong;public class ChineseCartoon { public String person(String arg1, String arg2) { if (arg1.equals("青莲地心火") && arg2.equals("陨落心炎")) { System.out.println("斗破苍穹---萧炎"); return "萧炎"; } return "123"; }}
- 这里的create方法有两个参数,分别是Class type和Callback callback。
- 其中Class type是指被代理类的字节码文件,因为有了被代理类的字节码后(即:被代理类的运行时类),就相当于可以获取被代理类的全部信息
- Callback callback是用于提供增强代码的,一般都是写一个接口的实现,通常情况下都是匿名内部类,这里我们一般不适用Callback接口,而是使用它的子接口MethodInterceptor的实现类。
生成代理类的工厂类 ProxyFactory:
package com.tong;import com.oracle.jrockit.jfr.Producer;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;public class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target; } // 通过该方法可以生成任意目标类所对应的代理类 public static Object getProxy(Object target) { // proxy就是我们创建的代理对象,这个对象可以执行被代理类中所有的方法,并且我们可以在代理对象中对被代理类的方法进行增强 Object proxy = Enhancer.create(target.getClass(), new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) { Object result = null; try { // 提供增强代码 System.out.println("[动态代理][日志] " + method.getName() + ",参数:" + Arrays.toString(objects)); //通过反射调用method对象所表示的方法,并获取该方法的返回值 //在具有指定参数的指定对象上调用此method对象表示的底层方法。 //此处就是通过target来确定调用的是具体哪个类中的方法 result = method.invoke(target, objects); System.out.println("[动态代理][日志] " + method.getName() + ",结 果:" + result); } catch (Exception e) { e.printStackTrace(); System.out.println("[动态代理][日志] " + method.getName() + ",异常:" + e.getMessage()); } finally { System.out.println("[动态代理][日志] " + method.getName() + ",方法执行完毕"); } return result; } }); // 返回代理对象 return proxy; }}
测试类 ProxyTest:
package com.tong;import org.junit.Test;public class ProxyTest { @Test public void test(){ Star star = new Star(); Star proxy = (Star)ProxyFactory.getProxy(star); proxy.sing(); System.out.println("-----------------分割线------------------"); proxy.dance(); System.out.println("-----------------分割线------------------"); // 创建被代理类的对象 ChineseCartoon chineseCartoon = new ChineseCartoon(); // 获取代理对象 ChineseCartoon proxy1 = (ChineseCartoon)ProxyFactory.getProxy(chineseCartoon); proxy1.person("青莲地心火","陨落心炎"); }}
运行结果:
结论:
当测试类中通过父类的引用proxy调用了方法proxy.sing()时,由于父类 Star类 被 子类(动态代理类 给继承了【即:代理子类已经重写了父类中的sing()】。所以,当使用父类引用proxy调用sing()方法时,实际执行的是动态代理类 (子类) 中的 sing()方法。而在这个动态代理类重写的方法中,又会去调用 MethodInterceptor接口的匿名实现类中重写的 intercept() 方法对父类方法的调用进行拦截【即:在子类中采用方法拦截的技术拦截父类所有的方法调用】。在这个方法中,可以实现对被代理方法的功能增强【即:织入横切逻辑】,最后通过method.invoke()反射技术来调用父类中的方法。
❤ 由于我们的代理类工厂中有参构造的参数是Object类型的,所以最终实现的效果是动态生成代理类对象。
tips:父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,父类引用是无法调用的。
MethodInterceptor接口的匿名实现类中重写的 intercept() 方法的官方文档解释:
JDK动态代理
代码:
接口 Calculator :
package com.tong.spring.calculator;public interface Calculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j);}
接口实现类 CalculatorImpl:
package com.tong.spring.calculator;public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { int result = i + j; System.out.println("方法内部 result = " + result); return result; } @Override public int sub(int i, int j) { int result = i - j; System.out.println("方法内部 result = " + result); return result; } @Override public int mul(int i, int j) { int result = i * j; System.out.println("方法内部 result = " + result); return result; } @Override public int div(int i, int j) { int result = i / j; System.out.println("方法内部 result = " + result); return result; }}
生产代理对象的工厂类 ProxyFactory:
public class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target; } // 通过该方法可以生成任意目标类所对应的代理类 public Object getProxy(){ //第一个参数,获取代理对象的类加载器 (类加载器是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载。所以,此处通过代理类或者被代理类获取到的类加载器都是同一个,或通过任何一个类获取到的类加载器都是同一个。) ClassLoader classLoader = this.getClass().getClassLoader(); //第二个参数,被代理对象实现的所有接口数组 Class<?>[] interfaces = target.getClass().getInterfaces(); //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用 //代理实例的调用处理程序 //第三个参数InvocationHandler的实现类,这里用了匿名内部类的方式 InvocationHandler invocationHandler = new InvocationHandler() { //重写InvocationHandler的invoke方法,他有三个参数可以供我们使用 @Override public Object invoke(Object proxy, Method method, Object[] args){ Object result = null; try { //method.getName(): 返回此method对象表示的方法的名称,作为字符串 System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args)); //通过反射调用method对象所表示的方法,并获取该方法的返回值 //在具有指定参数的指定对象上调用此method对象表示的底层方法。 //此处就是通过target来确定调用的是具体哪个类中的方法 result = method.invoke(target, args); System.out.println("[动态代理][日志] "+method.getName()+",结 果:"+ result); } catch (Exception e) { e.printStackTrace(); System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage()); } finally { System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕"); } return result; } }; //返回指定接口的代理类的实例,该实例将方法调用分派给指定的调用处理程序。 return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler); }}
测试类 ProxyTest:
package com.tong.proxy;import com.tong.spring.calculator.Calculator;import com.tong.spring.calculator.CalculatorImpl;import com.tong.spring.calculator.proxy.ProxyFactory;import org.junit.Test;public class ProxyTest { @Test public void testDynamicProxy(){ ProxyFactory factory = new ProxyFactory(new CalculatorImpl()); Calculator proxy = (Calculator) factory.getProxy(); //创建好了代理对象,代理对象就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强. proxy.add(1, 5);// proxy.div(1,0); }}
运行结果:
结论:
通过factory.getProxy()创建好了代理对象后,代理对象proxy就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强并通过反射调用被代理的同名方法。
编写不易,有帮到各位朋友理解的,点个赞再走哦!๑(≥▽≤)๑
来源地址:https://blog.csdn.net/weixin_43935152/article/details/130514814
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341