为什么JDK动态代理只能代理接口?

news2024/11/24 12:10:07

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

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

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

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

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

1、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。

2、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动态代理要高,但是它也需要额外的库依赖。

3、两者优缺点

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

JDK动态代理的优点:

  • JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。

  • JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。

  • 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。

JDK动态代理的缺点:

  • JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。

  • JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。

CGLIB代理的优点:

  • CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。

  • CGLIB代理可以代理没有实现接口的类。

CGLIB代理的缺点:

  • CGLIB代理需要引入外部依赖。

  • CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。

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

 

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

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

相关文章

第一章:Linux常用命令+VIM+SSH介绍+SSH密钥登录

目录 一、Linux常用命令 1&#xff09;# 与 $ 提示的区别 2&#xff09;ifconfig 3&#xff09; su 4&#xff09; cd 5&#xff09; 目录查看 6&#xff09; 查看文件内容 7&#xff09;创建目录及文件 8&#xff09;复制和移动 9&#xff09;其他 10&#xff09; …

Git 上传Github 超时问题

提交代码到GitHub总是超时&#xff0c;偶尔会直接上传成功。 提供一下解决方案 1.首先找到网络 2. 找到代理 3. 把自动检查设置全部关闭&#xff0c;然后打开手动设置代理&#xff0c;然后输入ip地址和你代理的端口号&#xff0c;保存即可。 4. 最后使用git push origin mast…

多边形偏移算法【Polygon Offsetting】

在本教程中&#xff0c;我们将描述一种膨胀或收缩多边形的算法。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 1、一般形式的同调 为了简单起见&#xff0c;让我们从一个形状开始&#xff0c;一个正方形。 我们可能熟悉通过放大或缩小形状来缩放形状的想法。 从技术…

Jmeter实现生成全局参数-随机数

我在做项目的过程中&#xff0c;用的比较多的是【前置处理中】-用户参数 步骤一&#xff1a;添加参数处理器 步骤二&#xff1a;填写项目中可能会用到的一些参数&#xff08;固定的或者随机的都可以&#xff09; teacher_name${__RandomString(1,赵钱孙李周吴郑王冯陈褚卫蒋沈…

Python指定函数参数和返回值的类型

先看一个简单的示例&#xff1a; # name指定为str类型&#xff0c;返回值为str类型 def greeting(name: str) -> str:return hello: nameif __name__ __main__:print(函数参数类型、返回值类型基本使用)s greeting(张三)print(s)try:s greeting(123) # 可以传递其他类…

学习网页设计html个人总结

学习网页设计html个人总结篇一 我在网上进行的逻辑教学工作已经完成了&#xff0c;闲暇无事之余&#xff0c;便想将个人的主页——《思东书屋》重新进行一下规划和设计&#xff0c;为此购买了几本有关网页设计和制作的书籍&#xff0c;开始学习网页的基本语言HTML。尽管网页制作…

postgresql 数据库 索引 介绍

postgresql 数据库 索引 介绍 文章目录 postgresql 数据库 索引 介绍前言一 什么是索引&#xff1f;二 简介三 索引的种类B-treeHash索引GiST索引GIN 索引BRIN 索引SP-GiST索引 CREATE INDEX1.大纲2.描述3. 参数UNIQUECONCURRENTLYIF NOT EXISTSINCLUDEnameONLYmethodcolumn_na…

Mysql之账号管理、建库以及四大引擎详解

目录 一、MySql数据库引擎 1.1 什么是数据库引擎&#xff1f; 1.2 MySQL常见数据库引擎 1.2.1.InnoDB(MySQL默认引擎) 1.2.2.MyISAM 1.2.3.MEMORY&#xff08;Heap&#xff09; 1.3 存储引擎查看 二、建库 2.1.默认数据库介绍 2.2.建库 2.3.查看数据库 2.4.删除数…

【嵌入式Qt开发入门】如何使用Qt进行文本读写——QFile读写文本

在很多时候我们需要读写文本文件进行读写&#xff0c;比如写个 Mp3 音乐播放器需要读 Mp3 歌词里的文本&#xff0c;比如修改了一个 txt 文件后保存&#xff0c;就需要对这个文件进行读写操作。本文介绍简单的文本文件读写&#xff0c;内容精简&#xff0c;让大家了解文本读写的…

linux 内核的学习

一、启动流程 二、进程调度 进程 进程控制块&#xff08;pid&#xff09;结构体 重要的信息 进程标识符 pid 状态&#xff1a;任务状态 优先级&#xff1a;相对其他进程 程序计数器&#xff1a;程序中即将被执行的下一条指令的地址 内存指针&#xff1a;包括程序代码…

echarts 柱状体点击空白处

点击红框里的空白区域&#xff1a; 再data里定义的idlist变量:idList: [800, 300, 500, 400, 800, 300, 500, 400, 800, 300, 500, 400, 800, 300,500, 400, 800, 300, 500, 400,]//以下是js代码var chartDom document.getElementById(echartsOne)this.myChart echarts.init…

数据产品经理的职业发展机会

在当今的数字时代&#xff0c;数据产品经理已成为许多企业中不可或缺的角色。随着越来越多的企业开始将数据应用于业务决策和产品开发中&#xff0c;数据产品经理的需求也不断增加。因此&#xff0c;学习成为一名数据产品经理成为了许多人的热门选择。下面说一说关于如何学习数…

tty(imx serial)(二)

基于linux-3.14.16 基于imx分析 一、串口设备注册 注册了一个uart设备驱动&#xff0c;和一个平台设备驱动 先一个一个看 1、serial_imx_driver平台设备驱动 看看匹配的设备树&#xff0c;imx6qdl.dtsi定义了很多&#xff0c;我们只看一个 如上图&#xff0c;设备树描述了…

基于 RK3399+fpga 的 VME 总线控制器设计(二)硬件和FPGA逻辑设计

3.2 FPGA 最小系统设计 FPGA 最小系统是指可以使 FPGA 正常工作的最基本的系统&#xff0c;主要包括电源电 路、配置电路、时钟和复位电路。本次设计使用的 FPGA 为紫光同创的 PG2L100H, 接下来具体介绍 FPGA 最小系统各个部分的电路设计。 &#xff08; 1 &#…

RflySim平台——高可信度的无人控制系统开发、测试与评估平台

01/RflySim平台背景 智能无人系统是一个复杂系统&#xff0c;涉及机械、控制、计算机、通信和材料等多个领域技术&#xff0c;而AI无疑是开发IUS所需的关键技术之一。随着新一代人工智能兴起&#xff0c;我们正面临一场新的工业革命&#xff0c;人类创造了各种无人系统&#x…

论文解读|点云分割中用于多尺度处理的金字塔结构

原创 | 文 BFT机器人 01 论文内容 这篇论文是关于点云分割中金字塔架构的多尺度处理的详细解读。 点云数据的语义分割是自动驾驶和其他应用中的关键任务。在这项工作中&#xff0c;作者指出了在点云分割中移动U形结构的紧迫需求和巨大好处&#xff0c;并受到图像分割领域最新进…

Fleet Farm EDI 需求详解

Fleet Farm 是一家成立于1955年的美国卖场&#xff0c;总部位于威斯康星州。作为一个全方位的零售商&#xff0c;Fleet Farm 提供了广泛的商品和服务&#xff0c;涵盖了农业用品、家居装饰、户外用品、汽车用品、园艺用品等多个领域。他们的使命是为客户提供高品质的产品和卓越…

《阿里大数据之路》读书笔记:第二章 日志采集

第二章 日志采集 一、浏览器的页面日志采集 浏览器的页面型产品/服务的日志采集可分为两大类&#xff1a; 页面浏览&#xff08;展现&#xff09;日志采集 指一个页面被浏览器加载呈现时采集的日志 此类日志是最基础的互联网日志 此类日志是目前所有互联网产品的两大基本指…

【Linux】LVS负载均衡群集 NAT模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LVS负载均衡群集 NAT模式 一、理解负载均衡群集的原理1.企业群集应用概述2.企业群集分类3.负载均衡群集架构4.负载均衡群集工作模式分析5.NAT模式6.TUN模式7.DR模式 二、LVS虚…

ESP32 BLE蓝牙服务端客户端通信

官方帮助见这里 BLE服务端命令 ATRESTORE//复位 ATBLEINIT2 //将esp32设置成蓝牙SERVER ATBLEGATTSSRVCRE //GATTS创建服务, 建议初始化后就建立GATTS, 建立连接在创建会报错 ATBLEGATTSSRVSTART //GATTS 开启全部服务。 ATBLEGATTSCHAR? //GATTS 发现服务特征 ATBLEADDR…