文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

通俗易懂 快速理解 JDK动态代理 和 cglib动态代理

2023-10-21 19:16

关注

没有实现接口或者不需要实现接口的类,我们可以通过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";    }}

生成代理类的工厂类 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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯