【代码】Java中的动态代理实战

news2024/11/22 21:19:41

文章目录

    • 1. JDK 动态代理
    • 2、CGLIB 动态代理

动态代理允许你在运行时创建代理对象,来代替原始对象执行某些操作。这在AOP(面向切面编程)中非常有用,用于实现日志记录、性能监控、事务管理等功能。
Java提供了两种主要的动态代理实现方法:基于接口的代理(JDK 动态代理)和基于类的代理(CGLIB)。
在这里插入图片描述

1. JDK 动态代理

JDK动态代理要求目标对象实现一个或多个接口,代理对象会实现这些接口并将方法调用转发给实际的目标对象。
首先,定义一个接口:

public interface UserDao {
    void addUser();
    void deleteUser();
}

然后,创建一个实现了该接口的目标类:

public class UserDaoImpl implements UserDao{
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}

再然后,添加一个切面,比如检查权限,记录日志等

public class MyAspect {
    public void checkPermissions(){
        System.out.println("检查权限...");
    }

    public void log(){
        System.out.println("记录日志...");
    }
}


接下来,创建一个实现了InvocationHandler接口的类,它将处理代理方法的调用:

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

public class JDKProxy implements InvocationHandler {

    private UserDao userDao;
    public Object createProxy(UserDao userDao){
        this.userDao = userDao;
        // 类加载器
        ClassLoader classLoader = JDKProxy.class.getClassLoader();
        // 被代理对象实现的所有接口
        Class[] clazz = userDao.getClass().getInterfaces();
        // 使用代理类进行增强,返回的是代理后的对象
        return Proxy.newProxyInstance(classLoader,clazz,this);
    }

    /**
     * 所有动态代理类的方法调用,都会交由invoke方法处理
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 声明切面
        MyAspect myAspect = new MyAspect();
        // 前增强
        myAspect.checkPermissions();
        // 在目标类上调用方法,并传入参数
        Object obj = method.invoke(userDao, args);
        // 后增强
        myAspect.log();
        return obj;
    }
}

最后,使用Proxy类创建代理对象:

public class JDKProxyTest {
    public static void main(String[] args) {
        // 创建代理对象
        JDKProxy jdkProxy = new JDKProxy();
        // 创建目标对象
        UserDao userDao = new UserDaoImpl();

        // 从代理对象中获取增强后的目标对象
        UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
        // 执行方法
        userDao1.addUser();;
        userDao1.deleteUser();
    }
}

输出将会是

检查权限...
添加用户
记录日志...
检查权限...
删除用户
记录日志...

2、CGLIB 动态代理

CGLIB动态代理允许在不修改目标类源代码的情况下创建代理。它通过生成目标类的子类来实现代理。

首先,定义一个目标类

public class UserDaoV2 {
    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }
}

然后,创建切面

public class MyAspect {
    public void checkPermissions(){
        System.out.println("检查权限...");
    }

    public void log(){
        System.out.println("记录日志...");
    }
}

创建 InvocationHandler:这是一个实现了 InvocationHandler 接口的类,用于拦截方法调用并执行自定义逻辑(切面)。

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 CglibProxy implements MethodInterceptor {
    public Object createProxy(Object target){
        // 创建动态类对象
        Enhancer enhancer = new Enhancer();
        // 确定需要增强的类,设置其父类
        enhancer.setSuperclass(target.getClass());
        // 添加回调方法
        enhancer.setCallback(this);
        // 返回创建的代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 声明切面
        MyAspect myAspect = new MyAspect();
        // 前增强
        myAspect.checkPermissions();
        // 在目标类上调用方法,并传入参数
        Object obj = methodProxy.invokeSuper(proxy,objects);
        // 后增强
        myAspect.log();
        return obj;
    }
}

最后,使用

import cn.diyai.proxy.dynamic_proxyV2.dao.UserDaoV2;

public class CglibProxyTest {
    public static void main(String[] args) {
        // 创建代理对象
        CglibProxy cglibProxy = new CglibProxy();

        // 创建目标对象
        UserDaoV2 userDao = new UserDaoV2();

        // 获取增强后的目标对象
        UserDaoV2 userDao1 = (UserDaoV2) cglibProxy.createProxy(userDao);
        // 执行方法
        userDao1.addUser();
        userDao1.deleteUser();
    }
}

输出结果为

检查权限...
添加用户
记录日志...
检查权限...
删除用户
记录日志...

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

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

相关文章

缓存的设计方式

问题情况: 当有大量的请求到内部系统时,若每一个请求都需要我们操作数据库,例如查询操作,那么对于那种数据基本不怎么变动的数据来说,每一次都去数据库里面查询,是很消耗我们的性能 尤其是对于在海量数据…

【0基础入门Python Web笔记】一、python 之基础语法、基础数据类型、复合数据类型及基本操作

一、python 之基础语法、基础数据类型、复合数据类型及基本操作 基础语法规则基础数据类型数字类型(Numbers)字符串类型(String)布尔类型(Boolean) 复合数据类型List(列表)Tuple&…

【C++代码】有序数组的平方,长度最小的子数组,螺旋矩阵 II--代码随想录

题目:有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 题解 数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的…

shell 编写一个带有进度条的程序安装脚本

需求 使用 shell 写一个 软件安装脚本,带有进度条 示例 #!/bin/bash# 模拟软件安装的步骤列表 steps("解压文件" "安装依赖" "配置设置" "复制文件" "")# 计算总步骤数 total_steps${#steps[]}# 安装进度的初…

JVM——类加载与字节码技术—类加载器+运行期优化

5.类加载器 jdk的类加载器具有层级关系。 启动类加载器》扩展类加载器》应用程序类加载器》自定义类加载器 对应类加载器只会负责加载对应目录的类。 双亲委派上级机制 应用程序类加载器加载一个类之前会先查询上级加载器是否已经加载过了该类。然后再让上级询问上上级。都…

代码随想录算法训练营第四十四天|LeetCode 309,714

目录 LeetCode 309.最佳买卖股票时机含冷冻期 动态规划五步曲: 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方向 5.打印dp数组 LeetCode 714.买卖股票的最佳时机含手续费 动态规划五步曲: 1.确定dp[i]的含义 2.找出递推公式 3.初始…

项目构建工具:CMake的核心用法

Golang有go mod、Python有pip、Java有maven。但C语言没有这么好用的包管理工具。当然Conan大概可以算是一个,但其也有自身的局限性,使用起来并不简单。 这就导致我们在写C代码的时候,老是要把心思放在怎么构建项目上。比如有一个项目&#x…

重磅!亚马逊将于10月再次举行秋季会员大促!

今年亚马逊7月份的Prime Day大促出乎卖家意料,效果出奇之好,大促确实有提振销量和信心的奇效。 而在近期,亚马逊宣布,将在今年10月,继续为亚马逊Prime会员带来“Prime秋季会员大促”。 19个国家(包括澳大…

使用BeanShell写入内容到文件【JMeter】

一、前言 ​ 在我们日常工作中,可能会遇到需要将请求返回的数据写入到文件中。在我们使用JMeter进行性能测试时,就经常能够遇到这种情况。要想达到这种目的,我们一般采取BeanShell后置处理器来将内容写入到文件。 二、提取 ​ 在目前大多数的…

使用代理突破浏览器IP限制

一、实验目的: 主要时了解代理服务器的概念,同时如何突破浏览器IP限制 二、预备知识: 代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站,特别是它具有一个cac…

【Python】从入门到上头—Python基础(2)

文章目录 一.基础语法1.编码2.标识符3.保留字4.注释5.行与缩进6.多行语句7.数字(Number)类型8.字符串(String)9.空行10.等待用户输入11.同一行显示多条语句12.多个语句构成代码组13.print 输出14.import 与 from...import 二.基本数据类型1.变量和赋值2.多个变量赋值3.标准数据…

win10某个软件字体模糊修复

1、在桌面找到该软件,右键选择属性,如下: 2、打开后选择兼容性--更改高DPI设置。 3、点击高DPI缩放替代--应用程序。 4、点击应用,然后退出属性设置。重新打开软件,发现软件字体变得清晰了。

MySQL 保存日期用哪种数据类型

写在前面 在设计数据库表时不可避免的需要用到时间类型,到底选择那种数据类型来表示时间是一个值的讨论的问题,本文就一起来看下! 1:能用哪些数据类型 1:字符串:不要用,占用空间大,至少需要19个字节&…

STM32CubeMx配置HAL库编码器测速

编码器概述 编码器是一种用来测量机械旋转或位移的传感器。它能够测量机械部件在旋转或直线运动时的位移位置或速度等信息,并将其转换成一系 列电信号。按照读出方式编码器可以分为接触式和非接触式两种;按照工作原理编码器可分为增量式和绝对式两类。编…

全局ID生成方式

全局ID生成方式 目录 1. 全局唯一id介绍 1.1 特点 2. 常见的全局唯一id生成策略 2.1 利用数据库自增字段生成id2.2 UUID2.3 Redis生成id2.4 zookeeper生成ID2.5 Twitter的snowflake算法 3. 面试题目:实现一个全局的ID生成器,注意线程安全 3.1 单例模式…

【PCL-9】AABB包围盒

当一个物体边与坐标轴平行时&#xff0c;生成的ABB最小外接立方体有偏差&#xff0c;故这里采用AABB算法。 示例代码&#xff1a; #include <pcl/features/moment_of_inertia_estimation.h> #include <vector> #include <pcl/io/pcd_io.h> #include <pc…

在 Pytorch 中使用 TensorBoard

机器学习的训练过程中会产生各类数据&#xff0c;包括 “标量scalar”、“图像image”、“统计图diagram”、“视频video”、“音频audio”、“文本text”、“嵌入Embedding” 等等。为了更好地追踪和分析这些数据&#xff0c;许多可视化工具应运而生&#xff0c;比如之前介绍的…

机器学习十大算法之七——随机森林

0 引言 集成学习&#xff08;ensemble learning&#xff09;是时下非常流行的机器学习算法&#xff0c;它本身不是一个单独的机器学习算法&#xff0c;而是通过在数据上构建多个横型&#xff0c;集成所有模型的建模结果&#xff0c;基本上所有的机器学习领域都可以看到集成学习…

华为OD机试 - 连续字母长度 - 字符串(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明4、再输入5、输出6、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08…

混币器——隐私交易的天堂,还是洗钱犯罪的聚集地?

据美国财政部官网&#xff0c;Tornado Cash 联创 Roman Storm 已被 FBI 和国税局逮捕&#xff0c;罪名是串谋洗钱、串谋经营未经许可的资金传输业务以及串谋违反制裁规定&#xff0c;另一创始人 Roman Semenov仍然在逃。 FBI局长Christopher A. Wray说&#xff1a;“今天的公告…