Jdk动态代理和Cglib动态代理的区别

一: 前言:

代理模式分为 静态代理 和 动态代理,今天我要讲的是动态代理的两种常见、也是被广泛使用的实现方式-------jdk动态代理 和 Cglib动态代理

二:Jdk动态代理实现分析:

结构示意图如下,我定义了一个UserService接口,有一个getUserName方法。然后定义了一个实现类UserServiceimpl实现该接口。
在这里插入图片描述
JDK动态代理步骤

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法

这里就直接上代码了。

UserService 代理的接口对象

public interface UserService {
    
    public void getUserName();

}

UserServiceimpl 代理的实现类

public class UserServiceimpl implements UserService{
    @Override
    public void getUserName() {
        System.out.println("老墨,强哥说他想吃鲨鱼");
    }
}

JDKProxy

public class JDKProxy implements InvocationHandler {

    Object target;

    public JDKProxy(Object target) {
        this.target = target;
    }

     /**
     * 该方法负责集中处理动态代理类上的所有方法调用。
     * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     *
     * @param proxy  代理类实例
     * @param method 被调用的方法对象
     * @param args   调用参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强方法
        System.out.println("告诉老墨我想吃鱼了");
        // 执行方法
        Object invoke = method.invoke(target, args);
        // 后置增强方法
        System.out.println("好勒,我待会给强哥送去");
        return invoke;
    }

}

main 方法


    public static void main(String[] args) throws Exception{
        // 创建实体类对象
        UserService userService=new UserServiceimpl();

        // 创建一个与代理对象相关联的InvocationHandler
        InvocationHandler UserHandler= new JDKProxy(userService);

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");

        //创建一个代理对象stuProxy来代理UserHandler,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        UserService jdkUserService=  (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{UserService.class},UserHandler);

        // 代理执行方法
        jdkUserService.getUserName();

    }
	打印:
			告诉老墨我想吃鱼了
			老墨,强哥说他想吃鲨鱼
			好勒,我待会给强哥送去
			
			进程已结束,退出代码0

可以看出这一行代码是我们jdk动态代理的关键代码,那么这一行代码到底做了什么呢?

UserService jdkUserService=  (UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{UserService.class},UserHandler);

我们通过System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”,“true”);这一行代码会生产临时对象的二进制文件,通过反编译成java文件。

Proxy0 代理对象

/**
	* 
	*/
public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
	
	/**
	* 首先我们看到他有一个有参构造方法,InvocationHandler var1,生产代理对象时,通过构造方法传递一个InvocationHandler 对象。
	*/
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void getUserName() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("byprocuration.spring.service.UserService").getMethod("getUserName");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

1.Proxy0 实现了UserService 接口,继承了Proxy 类
2. 有一个构造方法,通过构造方法传递一个InvocationHandler 对象,而这个对象就是我们实现InvocationHandler 接口的JDKProxy。并给父类Proxy的h属性赋值。

   protected InvocationHandler h;

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
  1. 有一大堆变量m0,m1,m2,m3,他们对应的就是方法名称。
  2. 当我们调用getUserName()方法时,他会调用super.h.invoke(this, m3, (Object[])null)时,其实也就是调用我们定义的JDKProxy的invoke方法。

总结:

  1. 在我们采用jdk动态代理时,jdk为我们生成了$Proxy0 代理类,这个类文件是放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。
  2. 当我们调用getUserName方法时,他是会先调用父类属性h的invoke方法。而在实例化时,会把我们定义的JDKProxy给h进行赋值。
  3. 我们会发现$Proxy0是需要实现UserService 接口的,也就相当于JDK动态代理是基于接口进行代理的。

3.Cglib动态代理实现分析:

使用Cglib代理需要先导入asm版本包,在实现MethodInterceptor接口

cglibProxy

public class cglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("告诉老墨我想吃鱼了");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println("好勒,我待会给强哥送去");
        return result;

    }
    
    public Object getProxy(Class clazz){
        //设置创建子类的类,即指定为哪个类产生代理类
        enhancer.setSuperclass(clazz);

        /*设置回调函数
         * setCallback设置被代理类的public非final方法被调用时的处理类
         * */
        enhancer.setCallback(this);
        //创建子类实例
        return enhancer.create();
    }
}

执行main方法调用

    public static void main(String[] args) {

        // 实例化CglibProxy对象
        cglibProxy cglibProxy=new cglibProxy();
        // //获取代理对象
        UserServiceimpl cglibProxyUserService =(UserServiceimpl)cglibProxy.getProxy(UserServiceimpl.class);
        cglibProxyUserService.getUserName();
    }
			
			打印:
			告诉老墨我想吃鱼了
			老墨,强哥说他想吃鲨鱼
			好勒,我待会给强哥送去

Cglib是通过Enhancer来创建代理类对象。且采用了非常底层的字节码技术( ASM),其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。

可以查看这篇文章去了解

总结:

最后我们总结一下JDK动态代理和Gglib动态代理的区别:

1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。

2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。

3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/4455.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FrIf-FrIf_Transmit发送流程【配置参数FrIfImmediate:立即传输还是解耦传输】和代码分析

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 1 FrIf_Transmit中的 PDU 的配置的传输模式2 代码分析1 FrIf_Transmit中的 PDU 的配置的传输模式 每当FrIf的上层模块想要请求特定 PDU 的传输时,它都会调用 FrIf_Transmit 函数。调用 FrIf_Transmit的时候传递…

C语言--文件操作

目录前言什么是文件程序文件数据文件文件指针FILE结构的维护文件的打开和关闭文件的打开方式文件的顺序读写fputcfgetcfputsfgetsfprintffscanf文件流 标准输入/输出流sscanf和sprintf前言 在讲文件操作之前&#xff0c;我们先来思考这个问题&#xff1a; 我们为什么要使用文件…

大数据技术之Spark(一)——Spark概述

大数据技术之Spark&#xff08;一&#xff09;——Spark概述 文章目录前言一、Spark基础1.1 Spark是什么1.2 Spark VS Hadoop1.3 Spark优势及特点1.3.1 优秀的数据模型和丰富计算抽象1.3.3 spark的特点1.4 Spark 运行环境1.5 Spark运行架构1.5.1 Driver1.5.2 Executor1.5.3 Mas…

Java设计模式-4、适配器模式

适配器模式 在我们的应⽤程序中我们可能需要将两个不同接⼝的类来进⾏通信&#xff0c;在不 修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。 这个中间件就是适配器。所谓适配器模式就是将⼀个类的接⼝&#xff0c;转换成客 户期望的另⼀个接⼝。它可以让原…

【协议】03、深度解剖之HTTP协议

协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的标准、约定或者规则的集合&#xff0c;超文本传输协议(HTTP)是一种通信协议&#xff0c;它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。使用的默认端口为80端口。浏览器的默认端口也是80端…

【操作系统】第二章:进程管理

第二章&#xff1a;进程管理 OVERVIEW第二章&#xff1a;进程管理一、进程与线程1.进程概述&#xff08;1&#xff09;进程PCB&#xff1a;&#xff08;2&#xff09;进程的组成&#xff1a;&#xff08;3&#xff09;进程的特征&#xff1a;2.进程的状态与转换&#xff08;1&a…

基于储能进行调峰和频率调节研究【超线性增益的联合优化】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

算法练习随记(三)

1.全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; 输入&#x…

如何用AI玩转IG广告,打造高互动的引流营销?

如何用AI玩转IG广告&#xff0c;打造高互动的引流营销&#xff1f;相信做引流的卖家&#xff0c;都有接触过IG广告&#xff0c;然而流量引过来了&#xff0c;怎么处理客户的私信&#xff1f; 私信对话是你与粉丝培养深度关系的管道&#xff0c;好的互动不仅能养成高黏着度的铁粉…

5 个Python高级特性让你在不知不觉中成为Python高手

你已经使用 Python 编程了一段时间&#xff0c;编写脚本并解决各种问题。是你的水平出色吗&#xff1f;你可能只是在不知不觉中利用了Python的高级特性。 从闭包&#xff08;closure&#xff09;到上下文管理器&#xff08;context managers&#xff09;&#xff0c;本文给出一…

Linked List

链表在力扣上的介绍&#xff1a;链表&#xff08;Linked List&#xff09;是最简单的线性的、动态数据结构。理解它是理解树结构、图结构的基础。区别于数组&#xff0c;链表中的元素不是存储在内存中连续的一片区域&#xff0c;链表中的数据存储在每一个称之为「结点」复合区域…

五、传输层

&#xff08;一&#xff09;TCP传输控制协议 可靠的、面向连接的字节流服务&#xff0c;全双工&#xff0c;有端口寻址功能 1、TCP的三种机制 1.使用序号对分段的数据进行标记&#xff0c;便于调整数据包 2.TCP使用确认、校验和和定时器系统提供可靠性 3.TCP使用可变大小的…

ACK Net Exporter 与 sysAK 出击:一次深水区的网络疑难问题排查经历

作者&#xff1a;谢石、碎牙 不久前的一个周五的晚上&#xff0c;某客户A用户体验提升群正处在一片平静中&#xff0c;突然&#xff0c;一条简短的消息出现&#xff0c;打破了这种祥和&#xff1a; “我们在ACK上创建的集群&#xff0c;网络经常有几百毫秒的延迟。" 偶发…

C++模板基础(四)

函数模板&#xff08;四&#xff09; ● 函数模板的实例化控制 – 显式实例化定义&#xff1a; template void fun(int) / template void fun(int) //header.h template<typename T> void fun(T x) {std::cout << x << std::endl; }//main.cpp #include&quo…

Python(白银时代)——文件操作

文件的基本操作 概念 在计算机中&#xff0c;文件是以 二进制 的方式保存在磁盘上的 文本文件 和 二进制文件 文本文件&#xff08;用记事本打开能直接能看懂的&#xff09; 可以使用 文本编辑软件查看 本质上还是二进制的,比如 Python的源码文件 二进制文件&#xff08;用…

并发编程(六)-AbstractExecutorService源码分析

一、AbstractExecutorService简介AbstractExecutorService是一个抽象类&#xff0c;实现了ExecutorService接口&#xff0c;提供了线程池的基本实现。它是Java Executor框架的核心类&#xff0c;提供了线程池的基本操作&#xff0c;如提交任务、管理线程池、执行任务等。自定义…

阻塞队列(BlockingQueue)的实现和使用

阻塞队列&#xff08;BlockingQueue&#xff09; 文章目录阻塞队列&#xff08;BlockingQueue&#xff09;阻塞队列的梗概解耦合和削峰填谷java代码实现一个阻塞队列阻塞队列的梗概 众所周知&#xff0c;队列是一种数据结构&#xff0c;符合先进先出的结构&#xff0c;先进先出…

【动态绘图】python可视化--丝滑版

✅作者简介&#xff1a;在读博士&#xff0c;伪程序媛&#xff0c;人工智能领域学习者&#xff0c;深耕机器学习&#xff0c;交叉学科实践者&#xff0c;周更前沿文章解读&#xff0c;提供科研小工具&#xff0c;分享科研经验&#xff0c;欢迎交流&#xff01;&#x1f4cc;个人…

鼎桥通信,拥抱基础创新的“高灵活性”时代

作者 | 曾响铃 文 | 响铃说 伴随数智化转型成为时代变革大方向&#xff0c;一批走在时代前端的数智化转型企业应运而生&#xff0c;不断丰富5G、物联网等新兴技术的应用场景&#xff0c;构建万智互联的产业生态。作为国内通信领域的引领者&#xff0c;鼎桥通信技术有限公司&a…

AF染料试剂Alexa fluor 680 PEG Biotin,AF680 PEG Biotin,荧光强度稳定利于多种荧光标记

文章关键词&#xff1a;AF染料试剂&#xff0c;AF680&#xff0c;PE-Biotin衍生物Alexa fluor 680 PEG Biotin&#xff0c;AF680 PEG Biotin | Alexa fluor 680-PEG-生物素| CAS&#xff1a;N/A | 纯度&#xff1a;95%试剂参数信息&#xff1a; CAS&#xff1a;N/A 外观&am…
最新文章