java动态代理介绍

1. 什么是 Java 动态代理?

Java 动态代理就像你的私人助理,除了处理你的请求外,还偷偷在旁边加些额外操作(比如记账、提醒)!在 Java 中,动态代理的存在使得我们无需直接修改业务代码就能给方法加“特效”,像是日志记录、性能监控,甚至可以实现一些自动化的复杂逻辑。动态代理不仅让代码变得更灵活,还让开发者可以专注在核心业务上。是不是很酷?

Java 动态代理主要通过 java.lang.reflect 包下的 Proxy 类和 InvocationHandler 接口实现。简单来说,就是通过反射实现代理类,在方法调用前、后可以偷偷加些额外操作,像一个“隐形的小跟班”。


2. 来点代码演示一下!

先来看下我们要代理的对象接口和实现类。假设我们现在要处理一个 UserService 接口:

// 定义接口 
public interface UserService { 
    void addUser(String name); 
}



 // 实现接口 
public class UserServiceImpl implements UserService { 
    @Override 
    public void addUser(String name) { 
        System.out.println("User added: " + name); 
    } 
}

这个 UserServiceImpl 是个正经的“好同事”,它只知道老老实实地执行 addUser。而我们又不想直接修改它,但希望在调用它的每次操作前后记录日志。这时候,动态代理就派上用场啦!我们定义一个“代理助手” UserServiceInvocationHandler

// 定义代理处理器 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
public class UserServiceInvocationHandler implements InvocationHandler { 
    private final Object target; 

    public UserServiceInvocationHandler(Object target) { 
        this.target = target; 
    } 
    
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        System.out.println("Before method: " + method.getName()); 
        Object result = method.invoke(target, args); 
        System.out.println("After method: " + method.getName()); return result; 
    } 

}

最后,我们需要在实际操作中让这个代理助手“跟班”到 UserServiceImpl 上,这就靠 Proxy.newProxyInstance()出手了:

import java.lang.reflect.Proxy; 

public class ProxyDemo { 
    public static void main(String[] args) { 
        UserService userService = new UserServiceImpl();
        UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService); // 创建代理对象 
        UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler); 
        // 通过代理对象调用方法 
        proxy.addUser("John Doe"); 
     }

}

运行这个小示例时,输出如下:

Before method: addUser
User added: John Doe
After method: addUser

每次 addUser 被调用前后,都会显示我们额外加上的“日志”信息!


3. 深入分析代理实现过程

让我们逐步拆解上面的代码片段:

  1. 接口和实现类UserService 是接口,而 UserServiceImpl 是它的忠实实现者。它只负责添加用户,一丝不苟、毫无花哨。

  2. 代理处理器UserServiceInvocationHandler 是关键点。它通过实现 InvocationHandler 接口的 invoke 方法,起到“看门人”的作用。在 invoke 方法中,使用 method.invoke(target, args) 来执行目标方法,并在前后输出日志。

  3. 代理对象创建Proxy.newProxyInstance 是代理工厂。它接收以下参数:

    • ClassLoader:用于加载代理类;
    • Class<?>[] interfaces:代理类要实现的接口(我们这里是 UserService);
    • InvocationHandler h:刚才定义的“看门人”,它负责在方法前后加点“调味料”。

最终得到的 proxy 就是代理对象,我们可以用它来“装扮”成 UserServiceImpl,在执行方法的前后悄悄进行日志记录。


4. Java 动态代理的幕后工作原理

在 JDK 动态代理机制中,Proxy.newProxyInstance() 其实是生成了一个代理类,这个类在后台帮我们动态生成并实现了 UserService 接口。这种方式虽然看似复杂,但让我们得以灵活控制对象方法的调用过程。

步骤分解
  • 代理类生成:Java 使用 Proxy 类生成代理类,其形式类似于以下伪代码:

public final class $Proxy0 extends Proxy implements UserService {
    public $Proxy0(InvocationHandler handler) {
        super(handler);
    }

    @Override
    public void addUser(String name) throws Throwable {
        handler.invoke(this, UserService.class.getMethod("addUser", String.class), new Object[]{name});
    }
}
  • 字节码生成:Java 动态代理通过字节码生成一个继承 Proxy 并实现目标接口的类。这让我们在代码编译阶段无需编写代理类,而是依赖 JVM 在运行时生成代理对象。

  • 方法调用处理:每次调用方法都会进入 InvocationHandler 的 invoke 方法,调用过程完全由 InvocationHandler 控制。


5. Java 动态代理的优缺点

优点
  • 无侵入性:不需要修改目标类就可以添加方法前后“特效”;
  • 灵活性:动态代理特别适合面向切面编程(AOP),像日志、性能监控、权限控制等操作都可以使用代理来轻松实现;
  • 简化代码:避免为每个方法都编写类似的切面逻辑。
缺点
  • 性能问题:反射调用相较直接调用有些开销;
  • 接口依赖:JDK 的动态代理只能处理实现了接口的类,无法代理普通类。

6. 总结与小提示

Java 动态代理提供了灵活的功能增强方式,但在使用时要注意性能和设计的权衡。大多数 AOP 框架(如 Spring AOP)底层都依赖于动态代理的思想,通过代理的“加持”实现功能增强。

希望通过这个介绍,你对 Java 动态代理有了更清晰的理解和运用思路!

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

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

相关文章

lvgl

lvgl 目录 lvgl Lvgl移植到STM32 -- 1、下载LVGL源码 -- 2、将必要文件复制到工程目录 -- 3、修改配置文件 将lvgl与底层屏幕结合到一块 -- lvgl也需要有定时器&#xff0c;专门给自己做了一个函数&#xff0c;告诉lvgl经过了多长时间&#xff08;ms&#xff08;毫秒&a…

第三十篇:TCP连接断开过程,从底层说明白,TCP系列五

上一篇《第二十九篇&#xff1a;图解TCP三次握手&#xff0c;看过不会忘&#xff0c;从底层说清楚&#xff0c;TCP系列四》说了TCP的三次握手&#xff0c;接下来我将讲解TCP四次挥手。 既然有连接就有断开&#xff0c;谈到这里&#xff0c;有的同学可能会想&#xff0c;不就是…

log4j 和 logback 冲突解决

很多springboot starter自带logback 如果我们要用log4j就要把logback排除掉 点idea的maven侧栏工具的分析依赖关系 然后我们要选中我们有冲突的模块&#xff0c;搜索logback 这时候我们发现有logback相关的依赖&#xff0c;在点一下&#xff0c;我们就在右边发现&#xff0c;原…

STM32--I2C通信

对于I2C通信会分为两大块来讲解&#xff0c;第一块,就是介绍协议规则,然后用软件模拟的形式来实现协议&#xff0c;第二块,就是介绍STM32的12C外设,然后用硬件来实现协议&#xff0c;因为12C是同步时序,软件模拟协议也非常方便。 在学12C之前,我们已经学习了串口通信&#xff…

openlayers 封装加载本地geojson数据 - vue3

Geojson数据是矢量数据&#xff0c;主要是点、线、面数据集合 Geojson数据获取&#xff1a;DataV.GeoAtlas地理小工具系列 实现代码如下&#xff1a; import {ref,toRaw} from vue; import { Vector as VectorLayer } from ol/layer.js; import { Vector as VectorSource } fr…

蓄电池在线监测系统 各大UPS铅酸蓄电池监测 保障安全

蓄电池的不断普及&#xff0c;确实推动了蓄电池监控和管理技术的持续升级。蓄电池检测系统的研发为我们带来了诸多好处&#xff0c;这些好处主要体现在以下几个方面&#xff1a; 一、提高蓄电池管理的智能化水平 蓄电池检测系统通过实时监测蓄电池的电压、电流、温度等关键参数…

ZEISS ATOS Q蓝光三维扫描仪高效把控零件质量检测【上海沪敖3D】

位于Bengaluru的施耐德电气工厂拥有一流的计量设备&#xff0c;可以检测所有供应商的零件。当时&#xff0c;他们在使用一款激光扫描设备进行质量检测&#xff0c;但是&#xff0c;该设备不便于携带&#xff0c;且检测时需要喷涂大量的显影液。此外&#xff0c;它需要被安装在夹…

docker基础使用创建固定硬盘大小为40G的虚拟机

在docker中创建的服务器&#xff0c;匹配出容器id&#xff0c;服务器ip&#xff0c;服务器核数&#xff0c;服务器内存&#xff0c;服务器硬盘空间 for i in $(docker ps | grep -aiE web | awk {print $1});do echo $i; docker inspect $i|grep -aiE ipaddr|tail -1|grep -ai…

医院信息化与智能化系统(7)

医院信息化与智能化系统(7) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

最新PHP网盘搜索引擎系统源码 附教程

最新PHP网盘搜索引擎系统源码 附教程&#xff0c;这是一个基于thinkphp5.1MySQL开发的网盘搜索引擎&#xff0c;可以批量导入各大网盘链接&#xff0c;例如百度网盘、阿里云盘、夸克网盘等。 功能特点&#xff1a;网盘失效检测&#xff0c;后台管理功能&#xff0c;网盘链接管…

使用freemarker实现在线展示文档功能开发,包括数据填充

首先&#xff0c;在这个独属于程序员节日的这一天&#xff0c;祝大家节日快乐【求职的能找到心仪的工作&#xff0c;已经工作的工资翻倍】。 ---------------------------------------------------------------回到正文-----------------------------------------------------…

状态栏黑底白字后如何实现圆角以及固定状态栏

如何实现如下效果: 上述是将状态栏实现黑底白字+圆角+状态栏固定的逻辑 具体代码patch如下: From 6a3b8ed5d3f49a38d8f9d3e488314a66ef5576b8 Mon Sep 17 00:00:00 2001 From: andrew.hu <andrew.hu@quectel.com> Date: Fri, 18 Oct 2024 16:43:49 +0800 Subject: [P…

Next.js14快速上手

文章目录 ***客户端***什么是Next项目在线地址官方文档项目创建查看项目目录结构app属于根目录 ***服务端***vercel数据库prisma 客户端 什么是Next Next.js 是一个用于构建全栈 Web 应用程序的 React 框架。您可以使用 React Components 来构建用户界面&#xff0c;并使用 Ne…

Unity引擎:游戏开发的核心力量

目录 引言 Unity引擎的发展历程 早期发展 跨平台支持 Unity引擎的核心特性 易用性 社区支持 跨平台能力 Unity在游戏开发中的应用 移动游戏 独立游戏 3A游戏 Unity的未来展望 高级图形和渲染技术 扩展现实&#xff08;XR&#xff09;支持 云服务和多人游戏 结论…

excel中,将时间戳(ms或s)转换成yyyy-MM-dd hh:mm.ss或毫秒格式

问题 在一些输出为时间戳的文本中&#xff0c;按照某种格式显示更便于查看。 如下&#xff0c;第一列为时间戳(s)&#xff0c;第二列是转换后的格式。 解决方案&#xff1a; 在公式输入框中输入&#xff1a;yyyy/mm/dd hh:mm:ss TEXT((A18*3600)/8640070*36519, "yyy…

Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks

Abstract 图像到图像转换是一类视觉和图形问题&#xff0c;其目标是使用对齐图像对的训练集来学习输入图像和输出图像之间的映射。 然而&#xff0c;对于许多任务&#xff0c;配对训练数据将不可用。 我们提出了一种在没有配对示例的情况下学习将图像从源域 X 转换到目标域 Y …

Android 15自定义设置导航栏与状态栏,EdgeToEdge适配

背景&#xff1a;android api 35&#xff0c;activity设置EdgeToEdge.enable((ComponentActivity) this)前提下 一、设置导航栏与状态栏颜色 设置的状态栏颜色&#xff0c;只需要设置fitsSystemWindows跟setOnApplyWindowInsetsListener xml设置&#xff1a; 代码&#xff1a;…

没有AWS账号能不能在手机上使用AWS服务吗?

关于“没有AWS账号能不能在手机上使用AWS服务”这个问题&#xff0c;答案是不行的。要使用AWS&#xff08;亚马逊云服务&#xff09;提供的云服务&#xff0c;无论是在电脑还是手机上&#xff0c;都必须先创建一个AWS账号。AWS提供的各种云计算资源&#xff0c;比如EC2&#xf…

51单片机——OLED显示图片

取模软件&#xff1a;链接:https://pan.baidu.com/s/1UcrbS7nU4bsawNxsaaULfQ 提取码:gclc 1、如果图片大小和格式不合适&#xff0c;可以先用Img2Lcd软件进行调整图片大小&#xff0c;一般取模软件使用的是.bmp图片&#xff0c;可以进行输出.bmp格式。软件界面如下&#xff1…

ubuntu编译kaldi和vosk

文章目录 前言一、开源框架的选取二、kaldi编译三、编译vosk方案一方案二 前言 由于工作需要语音识别的功能&#xff0c;环境是在linux arm版上&#xff0c;所以想先在ubuntu上跑起来看一看&#xff0c;就找了一下语音识别的开源框架&#xff0c;选中了vosk这个开源库&#xf…