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

这篇具有很好参考价值的文章主要介绍了通俗易懂 快速理解 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>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>2.1_3</version>
</dependency>

测试demo结构:

jdk动态代理实例和cglib动态代理区别,java,spring

代码:

被代理类 Star:

package com.tong;

/**
 * @author tong
 * @Description:被代理类
 */
public class Star {
    public void sing() {
        System.out.println("唱.......");
    }

    public void dance() {
        System.out.println("跳......");
    }
}

被代理类 ChineseCartoon:

package com.tong;

/**
 * 国漫:斗破苍穹
 *
 * @author tong
 * @Description:被代理类
 */
public class ChineseCartoon {

    public String person(String arg1, String arg2) {
        if (arg1.equals("青莲地心火") && arg2.equals("陨落心炎")) {
            System.out.println("斗破苍穹---萧炎");
            return "萧炎";
        }
        return "123";
    }
}

  • 这里的create方法有两个参数,分别是Class typeCallback 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;

/**
 * @author tong
 * @Description:生成代理类的工厂
 */
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() {
            /**
             * 这一步是整个过程的关键,代理类的实现要通过Enhancer类,我们需要通过Enhancer类中的create方法创建一个代理对象
             * @param o 是一个代理对象的引用 (即:增强对象)
             * @param method 是当前执行,即被拦截的被代理类方法
             * @param objects 是当前执行方法所用的参数,索引顺序即为方法定义时参数的顺序
             * @param methodProxy 指的是当前执行的方法的代理对象
             * @return 通过反射调用method对象所表示的方法, 并获取该方法的返回值
             */
            @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;

/**
 * @author tong
 * @Description:测试类
 */
public class ProxyTest {

    @Test
    public void test(){
        Star star = new Star();
        /* proxy就是我们创建的代理对象,这个对象可以执行被代理类中所有的方法,
        并且我们可以在代理对象中对被代理类的方法进行增强,
        注意这里使用了强转,因为getProxy方法的返回值是Object类型的对象*/
        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("青莲地心火","陨落心炎");
    }
}

运行结果:

jdk动态代理实例和cglib动态代理区别,java,spring

结论:

当测试类中通过父类的引用proxy调用了方法proxy.sing()时,由于父类 Star类子类(动态代理类 给继承了【即:代理子类已经重写了父类中的sing()】。所以,当使用父类引用proxy调用sing()方法时,实际执行的是动态代理类 (子类) 中的 sing()方法。而在这个动态代理类重写的方法中,又会去调用 MethodInterceptor接口的匿名实现类中重写的 intercept() 方法对父类方法的调用进行拦截【即:在子类中采用方法拦截的技术拦截父类所有的方法调用】。在这个方法中,可以实现对被代理方法的功能增强【即:织入横切逻辑】,最后通过method.invoke()反射技术来调用父类中的方法。

❤ 由于我们的代理类工厂中有参构造的参数是Object类型的,所以最终实现的效果是动态生成代理类对象。

tips:父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,父类引用是无法调用的。

MethodInterceptor接口的匿名实现类中重写的 intercept() 方法的官方文档解释:
jdk动态代理实例和cglib动态代理区别,java,spring

JDK动态代理

代码:

接口 Calculator :

package com.tong.spring.calculator;

/**
 * @author tong
 */
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;

/**
 * @author tong
 */
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:

/**
 * 动态代理有两种:
 * 1. jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口,在com.sun.proxy包下,类名为$proxy+数字 (例如:$proxy6)
 * 2. cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
 */
public class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    // 通过该方法可以生成任意目标类所对应的代理类
    public Object getProxy(){
        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:指定加载动态生成的代理类的类加载器(注:所有引入的第三方类库以及自己编写的java类 都是由 应用类加载器 负责加载的)
         【根类加载器(Bootstrap)> 扩展类加载器(Extension)> 系统类加载器(System)
         系统类加载器又称为应用类加载器】
         * 2、interfaces:获取目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象的接口的方法的过程,即代理类中如何重写接口中的抽象方法
         */
        //第一个参数,获取代理对象的类加载器 (类加载器是程序中默认的类加载器,一般来说,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){
                /**
                 * proxy:表示代理对象
                 * method:表示要执行的方法(代理对象需要实现的抽象方法,即其中需要重写的 和目标类同名的方法)
                 * args:表示要执行的方法的参数列表(method所对应方法的参数列表)
                 */
                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;

/**
 * @author tong
 */
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);
    }
}

运行结果:

jdk动态代理实例和cglib动态代理区别,java,spring

结论:

通过factory.getProxy()创建好了代理对象后,代理对象proxy就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强并通过反射调用被代理的同名方法。

编写不易,有帮到各位朋友理解的,点个赞再走哦!๑(≥▽≤)๑文章来源地址https://www.toymoban.com/news/detail-720529.html

到了这里,关于通俗易懂 快速理解 JDK动态代理 和 cglib动态代理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【spring】jdk动态代理和cglib动态代理的区别

    一、说明 1.spring aop中的动态代理主要有两种方式,jdk动态代理和cglib动态代理 2.从实现接口、继承父类的角度讨论区别 3.从限制角度讨论区别 4.从性能上讨论区别 二、区别 1.jdk动态代理只提供接口类的代理,如果目标类不是接口,只能用cglib代理 2.jdk动态代理会在运行时为目

    2024年02月16日
    浏览(40)
  • SpringAOP-说说 JDK动态代理和 CGLIB 代理

    Spring 的 AOP 是通过动态代理来实现的,动态代理主要有两种方式 JDK 动态代理和 Cglib 动态代理,这两种动态代理的使用和原理有些不同。 JDK 动态代理 Interface :JDK动态代理是基于接口的代理,它要求目标类实现一个接口。 InvocationHandler :InvocationHandler 是一个接口,可以通过

    2024年01月18日
    浏览(45)
  • java中的静态代理、jdk动态代理以及CGLIB 动态代理

    代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能 那以下文章主要谈三种代理模式, 分别是静态代理,jdk的动态代理,cglib的动

    2024年02月11日
    浏览(41)
  • 3_代理模式(动态代理JDK原生和CGLib)

    1.概念 代理模式(Proxy Pattern )是指 为其他对象提供一种代理,以控制对这个对象的访问 ,属于结构型模式。 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 使用代理模式主要有两个目的: 一是保护目标

    2024年01月17日
    浏览(37)
  • AOP、AspectJ、JDK动态代理、CGLIB

    AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、日志记录管理等。 AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类。 其中静态代理是

    2024年02月11日
    浏览(35)
  • springAop使用的动态代理是jdk还是cglib

    Spring AOP使用的动态代理可以是JDK动态代理或CGLIB动态代理,具体选择哪种代理方式取决于被代理的目标对象的类型和配置。 当被代理的目标对象实现了至少一个接口时,Spring AOP会默认使用JDK动态代理。JDK动态代理基于接口生成代理类,通过反射机制调用目标对象的方法。 而

    2024年02月09日
    浏览(37)
  • Spring之CGLIB和JDK动态代理底层实现

    目录 CGLIB 使用示例-支持创建代理对象,执行代理逻辑 使用示例-多个方法,走不同的代理逻辑 JDK动态代理 使用示例-支持创建代理对象,执行代理逻辑 ProxyFactory 如何自动在CGLIB和JDK动态代理转换 使用示例-使用CGLIB代理方式 使用示例-使用JDK动态代理方式 Spring会自动在JDK动态

    2024年04月25日
    浏览(28)
  • Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理

    大家都知道,AOP 底层是动态代理,而 Java 中的动态代理有两种实现方式: 基于 JDK 的动态代理 基于 Cglib 的动态代理 这两者最大的区别在于基于 JDK 的动态代理需要被代理的对象有接口,而基于 Cglib 的动态代理并不需要被代理对象有接口。 那么,Spring 中的 AOP 是怎么实现的

    2024年02月12日
    浏览(34)
  • 静态代理、jdk、cglib动态代理 搞不清? 看这个文章就懂了

    代理模式是一种比较好的理解的设计模式。简单来说就是 : 我们使用代理对象来增强目标对象(target obiect),这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 将核心业务代码和非核心的公共代码分离解耦,提高代码可维护性,让被代理

    2024年02月14日
    浏览(41)
  • 代理模式 静态代理和动态代理(jdk、cglib)——Java入职第十一天

            一个类代表另一个类去完成扩展功能,在主体类的基础上,新增一个代理类,扩展主体类功能,不影响主体,完成额外功能。比如买车票,可以去代理点买,不用去火车站,主要包括静态代理和动态代理两种模式。 代理类中包含了主体类 无法根据业务扩展,每一次

    2024年02月10日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包