Java的动态代理Proxy

2023-11-10

动态代理类是实现在运行时指定的接口列表的类,这样通过类实例上的一个接口的方法调用将被编码并通过统一接口分派到另一个对象。

先问一个问题?

数据库操作需要以下流程: 获取数据库连接->执行sql->提交事务->异常回滚事务->释放连接。

但是我们开发很少自己手动去做获取数据库连接、提交事务、回滚事务、释放连接等这些操作。

为什么我们不需要做这些操作???

那是有像Spring这样的框架,它底层用了动态代理技术,我们只需要写业务sql就可以了,其它像获取数据库连接、释放连接、提交事务、回滚事务等操作交给了代理类去完成,因为这些操作每个方法都是一样的,如果我们每个方法都去写那不是要疯掉。

代理类在执行被代理类方法的前后加上自己的操作。


传统数据库操作方式。

// 加载驱动类
Class.forName("com.mysql.jdbc.Driver");
Connection conn;
PreparedStatement ps1;
PreparedStatement ps2;
try {
    // 获取连接
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx", "root", "password");
    ps1 = conn.prepareStatement("update user set age = 1");
    ps2 = conn.prepareStatement("update user set money = 2");
    // 执行sql
    ps1.executeUpdate();
    ps2.executeUpdate();
    // 提交事务
    conn.commit();
 } catch (SQLException e) {
    // 回滚事务
    conn.rollback();
 } finally {
    // 关闭连接
    ps1.close();
    ps2.close();
    conn.close();
 }
}

看代码 动态代理到底怎么实现的

代理接口 EatSleep

// 接口 吃
public interface Eat {
    // 吃饭
    void eat();
}

// 接口 谁
public interface Sleep {
    // 睡觉
    void sleep(int hour);
}

被代理类 People

它实现了两个接口 EatSleep

public class People implements Eat, Sleep {
    @Override
    public void eat() {
        System.out.println("吃饭");
    }

    @Override
    public void sleep(int hour) {
        System.out.println("睡觉,时长:" + hour);
        if (hour <= 0) {
            // 睡眠时长小于0抛出异常
            throw new RuntimeException();
        }
    }
}

统一处理类 ProxyInvocationHandler

处理类需要实现InvocationHandler接口,获得invoke方法。

invoke方法有三个参数:代理类、被代理类的方法、被代理类的方法参数。

// 统一处理类
public class ProxyInvocationHandler implements InvocationHandler {
    private Object obj;

    public ProxyInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result;
        try {
            System.out.println("方法调用之前");
            result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
            System.out.println("统一处理异常");
            return null;
        } finally {
            System.out.println("方法调用之后");
        }
        return result;
    }
}

生成代理类

通过Proxy类的静态方法newProxyInstance生成代理类。

需要传递三个参数:类加载器、被代理接口数组、统一处理类。

public class Demo {
    public static void main(String[] args) {
        People obj = new People();
        Object proxyObj = Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new ProxyInvocationHandler(obj));

        Eat eatObj = (Eat) proxyObj;
        eatObj.eat();
        System.out.println();

        Sleep sleepObj = (Sleep) proxyObj;
        sleepObj.sleep(8);
        System.out.println();

        sleepObj.sleep(-1);
        System.out.println();
    }

// 运行结果    
// 方法调用之前
// 吃饭
// 方法调用之后

// 方法调用之前
// 睡觉,时长:8
// 方法调用之后

// 方法调用之前
// 睡觉,时长:-1
// 统一处理异常
// 方法调用之后
}

类加载器可以通过class对象的getClassLoader()方法获得。

还记得获取Class对象的几种方法吗???

这里的obj.getClass().getInterfaces()得到结果就是接口的class数组。就等同于

new Class[]{Eat.class, Sleep.class}

生成的代理类proxyObj可以向上转型为Eat接口和Sleep接口。不管调用Eateat()方法还是Sleepsleep()都会统一派发到InvocationHandler类的invoke方法,invoke方法中在调用被代理类的原方法之前和之后都可以进行一些操作,并且可以对被代理类的原方法的异常进行一些处理。


结语

动态代理非常重要,虽然平时可能很少用到,但是很多流行的框架都有用到它。动态代理还有一种实现方式CGLIB,它是一个强大的,高性能,高质量的Code生成类库,是一个开源项目,这个后续会跟大家分享。

关注微信公众号:小虎哥的技术博客,每天一篇文章,让你我都成为更好的自己

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java的动态代理Proxy 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 操作错误不会显示在 JSP 上

    我尝试在 Action 类中添加操作错误并将其打印在 JSP 页面上 当发生异常时 它将进入 catch 块并在控制台中打印 插入异常时出错 请联系管理员 在 catch 块中 我添加了它addActionError 我尝试在jsp页面中打
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 在mockito中使用when进行模拟ContextLoader.getCurrentWebApplicationContext()调用。我该怎么做?

    我试图在使用 mockito 时模拟 ContextLoader getCurrentWebApplicationContext 调用 但它无法模拟 here is my source code Mock org springframewo
  • 使用 JMF 创建 RTP 流时出现问题

    我正处于一个项目的早期阶段 需要使用 RTP 广播DataStream创建自MediaLocation 我正在遵循一些示例代码 该代码目前在rptManager initalize localAddress 出现错误 无法打开本地数据端口

随机推荐

  • DC/AC:单相方波全桥逆变电路设计原理及实验仿真

    电压源单向方波逆变电路可以是推挽式或全桥式 而后者的应用更加广泛 桥式电路又分为全桥和半桥 工作原理类似而略有差异 半桥电路较为简单 本文以单相全桥方波逆变电路进行分析 单相全桥逆变电路如图1所示 由一个大小为Ud的直流电压源和两个桥臂组成
  • 电路交换报文交换和分组交换

    http tech sina com cn other 2004 07 15 1604388226 shtml http tech sina com cn other 2004 07 15 1554388215 shtml 网络交换技术共经
  • 蓝桥杯——砝码称重(JAVA)

    题目 你有一架天平和 N个砝码 这 N 个砝码重量依次是 W 1 W 2 W N 请你计算一共可以称出多少种不同的重量 注意砝码可以放在天平两边 输入格式 输入的第一行包含一个整数 N 第二行包含 N 个整数 W 1 W 2 W 3 W N
  • 区块链草莽时代,致敬每一个实干者

    七月在野 八月在宇 九月在户 十月都在裁员 气温一点点降低 区块链行业也一步步临近冰点 区块链进入寒冬 留下的都是实干者 目前 区块链行业发展还存在各种各样的问题 比如人才储备不够 招不到人 法律的普及速度跟不上 技术应用落地还较为困难 而
  • Java程序员要掌握的前端:JavaScript篇

    第一篇HTML CSS在这里哦 Java程序员要掌握的前端 HTML CSS 第二章 Javascript 它是一种脚本语言 可以用来更改页面内容 控制多媒体 制作图像 动画等等 例子 修改页面内容 js 代码位置 引入 js 脚本 注意
  • 如何证明自己会python_如何判断python字符串出现次数?这几种方法你一定要学会...

    摘要 python应用阶段是实际的工作经验 现在如何判断python字符串出现次数 这几种方法你一定要学会 小编建议大家可以试着理解这些内容 也许对您的python学习有帮助 毕竟实践出真知 所以你要知道如何判断python字符串出现次数
  • 哲♂学三幻神带你学习ConstraintLayout(约束布局)

    哲 学三幻神带你学习ConstraintLayout 约束布局 标签 空格分隔 Tutorial Android ConstraintLayout 是什么 ConstraintLayout 约束布局 其实已经不算什么新东西了 很多同学应该知
  • 解决Gitlab不能向master分支push、merge的问题

    今天在向gitlab push代码的时候出现 remote rejected master gt master pre receive hook declined 这是因为gitlab默认开启了分支保护 以保护master分支不被其他用户随
  • 二、C语言初阶:函数

    2 函数 2 1 函数原型 函数原型通常放在头文件里面或者调用它的函数的前面 include
  • 从零开始学习Blazor

    什么是Blazor Blazor的优势和用途 简介 Blazor是一种基于WebAssembly的开源框架 它允许开发人员使用C 语言编写Web应用程序 Blazor可以在浏览器中运行C 代码 而无需使用JavaScript 该框架由微软开
  • 前端js循环修改对象属性,得到新数组数据属性是最后一个数(深拷贝与浅拷贝)

    项目场景 在前端项目的开发中 我将JSON格式的对象数组 例如 a 1 b 2 引入js文件 在js文件中通过循环修改对象数组中的数据 然后把新的数据push添加到新建数组中 这一过程涉及深拷贝与浅拷贝的问题 困扰了一下午 总算是解决了 特
  • HDMI PL驱动开发记录

    在Zynq UltraScale example的基础上 舍弃掉在PS端做驱动的方案 在PS端搭建工程 并利用串口进行配置 对于多路HDMI的不同输入输出控制起来较为方便 且利用PL来做驱动 结构更加清晰 在做HDMI驱动的时候要了解两个重
  • 回归模型的变量筛选与预测

    我眼中的回归变量筛选 变量筛选是回归建模过程关键的一步 由于变量间的相关性 必然会导致不同的筛选方法得到不同的模型 在所有变量筛选方法中 向前法 向后法以及逐步回归法的使用频率较高 因为这类方法操作简单 运算速度快 非常实用 这种方法选出的
  • java获取url锚点_定位与锚点

    文档流 普通流 上至下 左至右 浮动流 元素添加了float 定位流 添加了定位属性 定位 position static 默认值 absolute 绝对定位 脱离文档流 不占位 默认参考浏览器零点 relative 相对定位 占据文档流
  • c# 得到list符合某条件的索引值,排序

    请教 在List集合中怎么得到元素的索引值 参考 http www myexception cn c sharp 385022 html 这个可以用来读取窗口的多个textbox控件中内容 1 解决方案 2 你可以使用FindIndex方法
  • Centos8 部署 Mysql8.0及主从复制

    1 拉取镜像 root VM 24 9 centos docker pull mysql 如需拉取置指定版本镜像加上 版本号即可 root VM 24 9 centos docker pull mysql 7 6 1 创建本地文件用于挂载
  • C++ 异常处理

    C 异常 Exception 是指在程序运行时产生的特殊情况 例如 尝试除以零的操作 异常提供了一种转移程序控制权的方式 异常处理涉及到三个关键字 try catch throw throw 当问题出现时 程序会抛出一个异常 这是通过使用
  • VUE 定时请求接口数据

    1 设置定时器 mounted this timer setInterval function 执行内容 60000 2 清除定时器 beforeDestroy clearInterval this timer
  • Android平台深度学习--NNAPI

    转自 http blog sina com cn s blog 602f87700102y62v html 1 Android 8 1 API 27 NNAPI 人工智能神经网络API 如 TensorFlow 神经网络 API 能够向设备
  • Java的动态代理Proxy

    动态代理类是实现在运行时指定的接口列表的类 这样通过类实例上的一个接口的方法调用将被编码并通过统一接口分派到另一个对象 先问一个问题 数据库操作需要以下流程 获取数据库连接 gt 执行sql gt 提交事务 gt 异常回滚事务 gt 释放连