【面试系列】JDK动态代理和CGLIB静态代理

news2024/12/21 9:56:43

文章目录

  • 前言
  • JDK动态代理代码实例
  • Cglib 代理代码实例
  • 两者优缺点

前言

是否在面试过程中经常被问到Spring的代理的问题:比如说几种代理方式?两种代理方式的区别?或者问为什么JDK动态代理只能代理接口?

如果你能回答出来JDK动态代理的原理,然后引申Cglib 动态代理,那么这个面试官一定会对你刮目相看。

【面试系列】JDK动态代理和CGLIB静态代理 - Java技术债务

在Java中,动态代理是一种机制,允许在运行时动态地创建代理对象来代替某个实际对象,从而在其前后执行额外的逻辑。

为什么JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的。

当你使用Proxy类创建代理对象时,你需要指定一个接口列表来表示代理对象所应该实现的接口,这些接口就成为代理对象的类型。

具体来说,代理对象的方法调用会被转发到实现InvocationHandler接口的类中的invoke()方法。这个invoke()方法接受三个参数:代理对象本身、被调用的方法对象和方法的参数数组。invoke()方法需要返回被代理方法调用的结果。

由于代理对象的类型是由接口列表决定的,因此只有实现了接口的类才能被代理。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB。

JDK动态代理代码实例

下面是一个简单的示例代码,展示了如何使用JDK动态代理来创建代理对象。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyDemo {
    public static void main(String[] args) {
        RealObject real = new RealObject();
        InvocationHandler handler = new DynamicProxy(real);

// 创建代理对象
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class<?>[] { MyInterface.class },
                handler);

// 调用代理对象的方法
        proxy.doSomething();
    }
}

interface MyInterface {
    void doSomething();
}

class RealObject implements MyInterface {
    public void doSomething() {
        System.out.println("RealObject doSomething");
    }
}

class DynamicProxy implements InvocationHandler {
    private Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}

在上面的代码中,RealObject实现了MyInterface接口,它是我们要代理的实际对象。DynamicProxy类实现了InvocationHandler接口,并在invoke()方法中添加了额外的逻辑,用于在代理对象方法调用前后执行。

main()方法中,我们使用Proxy.newProxyInstance()方法创建代理对象。我们指定了MyInterface接口作为代理对象类型,并将DynamicProxy对象作为代理对象的InvocationHandler

最后,我们调用代理对象的doSomething()方法,并观察控制台输出的结果。

需要注意的是,代理对象的方法调用都会被转发到DynamicProxy类的invoke()方法中进行处理,因此在这个示例中,实际的RealObject对象的doSomething()方法的执行是在invoke()方法中通过反射进行的。

总结一下,JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的,代理对象的类型由接口列表决定。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB。

Cglib 代理代码实例

以下是CGLIB代理的示例代码。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBProxyDemo {
    public static void main(String[] args) {
        RealObject real = new RealObject();
        MethodInterceptor handler = new CGLIBProxy(real);

// 创建代理对象
        RealObject proxy = (RealObject) Enhancer.create(
                RealObject.class,
                handler);

// 调用代理对象的方法
        proxy.doSomething();
    }
}

class CGLIBProxy implements MethodInterceptor {
    private Object target;

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

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method invocation");
        Object result = proxy.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}

在上面的示例中,我们使用CGLIB的Enhancer类和MethodInterceptor接口来创建代理对象。RealObject类不再需要实现接口,而是直接作为代理对象的类型。在CGLIBProxy类中,我们实现了MethodInterceptor接口,并在intercept()方法中添加了额外的逻辑。

main()方法中,我们使用Enhancer.create()方法创建代理对象。我们指定了RealObject类作为代理对象类型,并将CGLIBProxy对象作为代理对象的MethodInterceptor。最后,我们调用代理对象的doSomething()方法,并观察控制台输出的结果。

需要注意的是,CGLIB代理使用字节码技术来生成代理对象,因此它的效率比JDK动态代理要高,但是它也需要额外的库依赖。

两者优缺点

JDK动态代理和CGLIB代理都有它们自己的优缺点。

  • JDK动态代理的优点:
    • JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
    • JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
    • 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
  • JDK动态代理的缺点:
    • JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
    • JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
  • CGLIB代理的优点:
    • CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
    • CGLIB代理可以代理没有实现接口的类。
  • CGLIB代理的缺点:
    • CGLIB代理需要引入外部依赖。
    • CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。

综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。


--------------------------------------------------------------欢迎叨扰此地址---------------------------------------------------------------

本文作者:Java技术债务
原文链接:https://cuizb.top/myblog/article/detail/1689928106
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vortex Indicator 构建交易策略

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

Day 40算法记录|dp完全背包问题 06

完全背包问题 基本知识01背包完全背包 518. 零钱兑换 II377. 组合总和 Ⅳ 基本知识 01背包 二维&#xff0c;两层for循环的循序&#xff0c;物品和背包不重要。 d p [ i ] [ j ] m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w e i g h t [ i ] ] v a l u e [ i…

docker基础6——制作镜像(dockerfile)

文章目录 一、基本了解1.1. 基于centos构建镜像1.2 基于alpine制作镜像 二、常用指令三、制作httpd镜像 一、基本了解 Dockerfile 是一个文本格式的配置文件&#xff0c;可以使用Dockerfile 快速创建自定义镜像。Dockerfile 由一行行命令语句组成&#xff0c;并且支持以 # 开头…

Python实现HBA混合蝙蝠智能算法优化XGBoost分类模型(XGBClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝙蝠算法是2010年杨教授基于群体智能提出的启发式搜索算法&#xff0c;是一种搜索全局最优解的有效方法…

PROFINET转TCP/IP网关profinet网线接头接法

大家好&#xff0c;今天要和大家分享一款自主研发的通讯网关&#xff0c;捷米JM-PN-TCPIP。这款网关可是集多种功能于一身&#xff0c;PROFINET从站功能&#xff0c;让它在通讯领域独领风骚。想知道这款网关如何实现PROFINET和TCP/IP网络的连接吗&#xff1f;一起来看看吧&…

MySQL:JDBC编程

在实际开发中&#xff0c;SQL很少是手动输入的&#xff0c;绝大多数的SQL都是通过代码&#xff0c;自动执行的。这个时候就需要其他编程语言来操作数据库服务器。 自己写一个数据库客户端是非常容易的&#xff0c;各种数据库本身就提供了一系列的API&#xff0c;但是自己写一个…

ActivityWatch配置跨平台同步(放弃)

老版本探索 老版本启动aw_qt.exe报错如下&#xff1a; 解决方案: ActivityWatch\activitywatch\PyQt5\Qt\plugins\platforms 复制到 ActivityWatch\activitywatch 下面重新启动aw_qt.exe即可解决 最新版 具体内容如下&#xff1a; Traceback (most recent call last): …

【前端】解决element-ui的form组件resetFields()方法不生效的问题

问题 使用element-ui的form组件&#xff0c;在输入栏较多的时候&#xff0c;有时resetFields只能重置一部分输入框的值 解决 给每个值赋值初始值为空即可 例如&#xff0c;需要清除的:model"queryForm.bgnTime"、:model"queryForm.endTime"等一系列的值…

基于Java+SpringBoot+vue前后端分离阿博图书馆管理系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

【Boost搜索引擎项目】

文章目录 一、项目流程二、项目展示 一、项目流程 1.编写数据去标签模块–parser.cc 将去标签之后干净文档以title\3content\3url\ntitle\3content\3url\n格式放入同一文件中。 2.建立索引模块–index.hpp 读取处理好的行文本文件进行分词、权重计算等操作&#xff0c;在内存中…

ICMP_1靶场详解

ICMP_1靶场复盘 这个靶场还是很有意思的&#xff0c;尤其在提权阶段。 靶场下载地址&#xff1a;https://download.vulnhub.com/icmp/icmp.ova.gz arp-scan --localnet首先扫描ip&#xff0c;登陆后发现是个这样的页面。 我们发现这个页面在提醒我们的就是出现了monitorr 然…

如何部署MHA高可用MySQL数据库

目录 一、MHA概念 MHA 的组成 MHA 的特点 MHA工作原理 二、部署MySQL数据库MHA高可用 第一步 关闭所有防火墙和安全机制 第二步 修改主数据库配置文件 第三步 修改从数据库的配置文件 第四步 重启服务 第五步 优化所有数据库路径 第六步 授权所有数据库节点 第七步…

nodejs+vue+elementui在线课程知识点管理系统

前端技术&#xff1a;nodejsvueelementui,视图层其实质就是vue页面&#xff0c;通过编写vue页面从而展示在浏览器中&#xff0c;编写完成的vue页面要能够和控制器类进行交互&#xff0c;从而使得用户在点击网页进行操作时能够正常。 可以设置中间件来响应 HTTP 请求。 对于之前…

C++ 使用tinyxml库处理XML文件

文章目录 前言一、下载tinyxml二、创建一个XML文件三、XML文件增加数据四、修改XML文件五、解析XML文件六、XML文件删除数据总结 前言 TinyXML是一个开源的解析XML的解析库&#xff0c;能够用于C&#xff0c;能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件&…

解决使用@Field注解配置分词器失效问题(Spring Data Elasticsearch)

问题复现&#xff1a;插入数据时&#xff0c;实体类配置的Field注解没有生效 实体类&#xff1a; package cn.aopmin.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import…

机器学习李宏毅学习笔记38

文章目录 前言一、生成式学习二、各个击破和一次到位的对比1.各个击破&#xff08;Autoregressive(AR) model&#xff09;2.一次到位&#xff08;Non-autoregressive(NAR) model&#xff09; 总结 前言 生成式学习两种策略&#xff1a;各个击破/一次到位 有结构的生成物比如语…

【vue3】常见的使用vue3创建项目的几种方法

1、使用ui界面创建&#xff0c;winr打开命令提示符&#xff0c;输入vue ui 2、winr打开命令提示符&#xff0c;输入vue create 项目名称 3、使用脚手架创建 其中方法一&#xff0c;方法二是使用npm run serve来运行的&#xff0c;方法三是使用npm run dev运行的

基于MOT数据集的高精度行人检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于MOT数据集的高精度行人检测系统可用于日常生活中检测与定位行人目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的行人目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训练数据集&…

react native 本地存储 AsyncStorage

An asynchronous, unencrypted, persistent, key-value storage system for React Native. Async Storage 只能用来储存字符串数据&#xff0c;所以为了去储存object类型的数据&#xff0c;得先进行序列化&#xff08;JSON.stringify()&#xff09;当你想要使用数据的时候&…

redis(9):spring里面使用redis

1 创建一个mave项目 自行创建一个maven项目 2 修改pom.xml <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven…