洛阳市宜阳建设局网站/短链接生成器
Java 反射机制是 Java 语言提供的一种能力,允许程序在运行时查询、访问和修改它自己的结构和行为。反射机制非常有用,但同时也需要谨慎使用,因为它可能会带来性能开销和安全风险。
以下是 Java 反射机制的一些关键概念和用法:
-
Class 类:Java 中的每个类都有一个与之对应的
Class
类对象。通过这个Class
对象,你可以获取类的名称、访问修饰符、字段、方法、构造函数等信息。 -
获取 Class 对象:
- 直接使用类名调用
.class
:Class<?> clazz = MyClass.class;
- 使用
.class
属性:Class<?> clazz = MyClass.class;
- 通过实例调用
getClass()
方法:Class<?> clazz = myObject.getClass();
- 使用
forName()
方法:Class<?> clazz = Class.forName("MyClass");
这个方法会通过类的全名来查找并加载类。
- 直接使用类名调用
-
访问字段:
- 获取字段对象:
Field field = clazz.getField("fieldName");
- 设置字段值:
field.set(object, value);
- 获取字段值:
Object value = field.get(object);
- 获取字段对象:
-
访问方法:
- 获取方法对象:
Method method = clazz.getMethod("methodName", parameterTypes);
- 调用方法:
Object result = method.invoke(object, args);
- 获取方法对象:
-
访问构造函数:
- 获取构造函数对象:
Constructor<?> constructor = clazz.getConstructor(parameterTypes);
- 创建实例:
Object instance = constructor.newInstance(args);
- 获取构造函数对象:
-
注解(Annotations):
- 反射可以用来读取类的注解信息,这在很多框架中被广泛使用,比如 Spring。
-
泛型和数组:
- 反射同样可以处理泛型和数组类型。
-
安全问题:
- 反射可以绕过编译时的访问控制,因此可能会访问到一些本应受保护的成员。
-
性能问题:
- 反射操作通常比直接代码调用要慢,因此在性能敏感的应用中应谨慎使用。
-
动态代理:
- 反射机制是 Java 动态代理的基础,允许在运行时创建接口的代理实例。
反射机制在很多高级应用场景中非常有用,比如框架的实现、依赖注入、单元测试等。然而,由于它可能带来的安全和性能问题,开发者在使用时应权衡其利弊。
1. 使用反射实现一个动态代理案例
在 Java 中,动态代理是一种在运行时创建代理对象的技术,它允许我们为接口的实现类添加额外的行为。以下是使用 Java 反射实现动态代理的一个简单例子。
假设我们有一个接口 SomeService
和它的实现类 SomeServiceImpl
:
public interface SomeService {void doSomething();
}public class SomeServiceImpl implements SomeService {public void doSomething() {System.out.println("Doing something...");}
}
现在,我们想要创建一个代理类,它在调用 SomeService
的方法之前和之后添加一些额外的日志信息:
import java.lang.reflect.*;public class DynamicProxyExample {public static void main(String[] args) {// 创建 SomeServiceImpl 的实例SomeService someService = new SomeServiceImpl();// 创建代理对象SomeService proxyInstance = (SomeService) Proxy.newProxyInstance(someService.getClass().getClassLoader(),someService.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());// 调用原始方法Object result = method.invoke(someService, args);System.out.println("After method: " + method.getName());return result;}});// 使用代理对象调用方法proxyInstance.doSomething();}
}
在这个例子中,我们使用了 Proxy.newProxyInstance()
方法来创建代理对象。这个方法需要三个参数:
- 类加载器:
someService.getClass().getClassLoader()
- 接口数组:
someService.getClass().getInterfaces()
- 一个实现了
InvocationHandler
接口的匿名类:这个类重写了invoke()
方法,该方法会在代理对象的方法被调用时执行。
invoke()
方法有三个参数:
proxy
:代理对象的实例。method
:被调用的方法的Method
对象。args
:传递给被调用方法的参数数组。
在 invoke()
方法中,我们可以添加任何我们想要的额外行为,比如日志记录,然后通过调用原始对象的相应方法来执行实际的操作。
请注意,这个例子中的代理只能用于实现了接口的对象。如果你需要代理一个类而不是接口,你可能需要使用其他技术,比如 CGLIB 库。
2. 使用CGLIB库实现动态代码案例
CGLIB(Code Generation Library)是一个强大的高性能代码生成库,用于在运行时扩展 Java 类和实现接口。与 Java 原生的动态代理不同,CGLIB 可以代理没有实现接口的类,并且可以添加方法拦截。
以下是使用 CGLIB 实现动态代理的一个简单例子:
首先,你需要将 CGLIB 库添加到你的项目中。如果你使用 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version> <!-- 请使用最新的版本号 -->
</dependency>
然后,我们可以创建一个 CGLIB 动态代理的示例:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.InvocationHandler;public class CglibDynamicProxyExample {public static void main(String[] args) {// 创建目标对象SomeServiceImpl target = new SomeServiceImpl();// 创建 Enhancer 对象Enhancer enhancer = new Enhancer();// 设置父类为 SomeServiceImplenhancer.setSuperclass(SomeServiceImpl.class);// 设置回调函数,即拦截器enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());// 调用原始方法Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}});// 创建代理对象SomeServiceImpl proxyInstance = (SomeServiceImpl) enhancer.create();// 使用代理对象调用方法proxyInstance.doSomething();}
}interface SomeService {void doSomething();
}class SomeServiceImpl implements SomeService {public void doSomething() {System.out.println("Doing something...");}
}
在这个例子中,我们使用了 CGLIB 的 Enhancer
类来创建代理对象:
- 创建
Enhancer
对象。 - 使用
setSuperclass()
方法指定要代理的类。 - 使用
setCallback()
方法设置一个实现了MethodInterceptor
接口的匿名类。这个拦截器会在代理对象的方法被调用时执行。 - 使用
create()
方法创建代理对象。
intercept()
方法有四个参数:
obj
:被代理的对象。method
:被调用的方法的Method
对象。args
:传递给被调用方法的参数数组。proxy
:一个MethodProxy
对象,可以用来调用原始方法。
在 intercept()
方法中,我们可以添加任何我们想要的额外行为,然后通过调用 proxy.invokeSuper(obj, args)
来执行原始方法。
CGLIB 动态代理提供了一种强大的机制,可以在不修改原始类代码的情况下,为类添加额外的功能。这在很多框架中被广泛使用,比如 Spring 的 AOP 模块。
3. 使用动态代理实现模拟 Spring 的 AOP 实现案例
在 Spring 框架中,AOP(面向切面编程)是一种编程范式,它允许你将横切关注点(如日志记录、安全性、事务管理等)与业务逻辑分离。Spring AOP 通过代理机制实现,可以是 JDK 动态代理或 CGLIB 代理。
这里我们将通过 Java 原生的动态代理来模拟一个简单的 Spring AOP 实现。假设我们有一个服务接口 SomeService
和它的实现 SomeServiceImpl
:
public interface SomeService {void doSomething();
}
public class SomeServiceImpl implements SomeService {@Overridepublic void doSomething() {System.out.println("Doing something...");}
}
接下来,我们将创建一个切面(Aspect),其中包含一个方法,该方法在目标方法执行前后添加日志:
public class LoggingAspect {public void beforeAdvice(JoinPoint joinPoint) {System.out.println("Before " + joinPoint.getMethod().getName());}public void afterAdvice(JoinPoint joinPoint) {System.out.println("After " + joinPoint.getMethod().getName());}
}
为了模拟 Spring AOP 的行为,我们定义一个 JoinPoint
接口,它将用于传递方法调用的相关信息:
public interface JoinPoint {Method getMethod();Object getTarget();
}
然后,我们定义一个 JoinPointImpl
类来实现 JoinPoint
接口:
import java.lang.reflect.Method;public class JoinPointImpl implements JoinPoint {private Method method;private Object target;public JoinPointImpl(Method method, Object target) {this.method = method;this.target = target;}@Overridepublic Method getMethod() {return method;}@Overridepublic Object getTarget() {return target;}
}
最后,我们将创建一个 AspectProxy
类来生成代理对象,并在代理对象的方法调用中应用切面逻辑:
import java.lang.reflect.*;public class AspectProxy implements InvocationHandler {private Object target;private LoggingAspect loggingAspect;public AspectProxy(Object target, LoggingAspect loggingAspect) {this.target = target;this.loggingAspect = loggingAspect;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {JoinPoint joinPoint = new JoinPointImpl(method, target);loggingAspect.beforeAdvice(joinPoint);Object result = method.invoke(target, args);loggingAspect.afterAdvice(joinPoint);return result;}public static Object createProxy(Object target, LoggingAspect aspect) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new AspectProxy(target, aspect));}
}
现在,我们可以创建一个测试类来演示如何使用这个模拟的 AOP 实现:
public class AopDemo {public static void main(String[] args) {SomeService someService = new SomeServiceImpl();LoggingAspect loggingAspect = new LoggingAspect();// 创建代理对象SomeService proxy = (SomeService) AspectProxy.createProxy(someService, loggingAspect);// 使用代理对象调用方法proxy.doSomething();}
}
在这个模拟的 AOP 实现中,我们通过 AspectProxy
类的 invoke()
方法在目标方法调用前后添加了日志记录。这种方式展示了如何在不修改原始类代码的情况下,通过代理机制实现 AOP 功能。
请注意,这只是一个简单的示例,真实的 Spring AOP 实现更为复杂,支持更多的功能如切入点表达式、通知类型(前置、后置、异常抛出、环绕等)、切面优先级等。
4. 总结反射在开发中的实际应用场景
Java 反射机制在软件开发中有着广泛的应用,以下是一些常见的实际应用场景:
-
框架开发:许多框架如 Spring 使用反射来实现依赖注入、AOP(面向切面编程)等核心功能。
-
动态代理:通过反射可以创建动态代理,用于实现方法拦截、日志记录、事务管理等功能。
-
单元测试:在进行单元测试时,反射可以用来访问和修改私有成员,以便测试通常无法直接访问的内部逻辑。
-
插件系统:应用程序可以利用反射动态加载和使用插件,从而扩展应用程序的功能。
-
配置文件解析:反射可以用于将配置文件中的设置映射到程序中的类和对象上。
-
对象序列化和反序列化:一些序列化框架(如 JSON、XML 序列化库)使用反射来动态读取和设置对象的属性。
-
泛型和集合操作:反射可以用于操作泛型类型,以及在运行时动态地操作集合。
-
注解处理:Java 反射可以用来读取和处理注解,这在编译时和运行时都很常见,例如用于生成代码或配置元数据。
-
动态类加载:在需要动态加载类的情况下,反射可以用来加载字节码并创建类的实例。
-
类型检查和类型转换:反射可以用来在运行时检查对象的类型,或者将对象转换为不同的类型。
-
访问和修改私有成员:在某些情况下,可能需要访问或修改类的私有成员,反射提供了这样的能力。
-
实现反射 API:Java 提供了丰富的反射 API,可以用来查询类的信息、创建对象实例、调用方法、访问字段等。
-
动态调用方法:在某些应用中,可能需要根据方法名字符串来动态调用方法,反射提供了这样的机制。
-
实现通用的数据处理:反射可以用来编写通用的数据访问层,处理不同实体的 CRUD 操作,而不需要为每个实体编写特定的代码。
-
实现工厂模式:反射可以用于实现工厂模式,根据字符串标识来创建对象实例。
反射机制虽然强大,但使用时需要注意性能开销和安全问题。在设计系统时,应权衡反射带来的灵活性和潜在的负面影响。
5. 最后
初学者在学习反射时,会无从下手,这很正常,因为在学习的过程中,没有实际的应用场景可以训练,这就是为什么我们要去学习优秀框架源码的原因,因为反射多数用在构建框架底层结构中被使用到,在应用开发时见不到,都被封装了,那我们为什么还要去了解呢,这个原因是因为很多公司会自定义满足自身要求的框架,而大多数都是基于开源框架做二次开发,这就需要充分理解开源框架的实现原理,也就会用到反射,在当下这个环境下,你懂的。欢迎关注威哥爱编程,我们一起成长。
相关文章:

Java 反射机制与Spring框架的那点事
Java 反射机制是 Java 语言提供的一种能力,允许程序在运行时查询、访问和修改它自己的结构和行为。反射机制非常有用,但同时也需要谨慎使用,因为它可能会带来性能开销和安全风险。 以下是 Java 反射机制的一些关键概念和用法: Cl…...

计算机网络面试题3
四次挥手 断开连接需要四次挥手 1.客户端发送一个FIN(SEQx)标志的数据包到服务端,用来关闭客户端到服务端的数据传送, 然后客户端进入FIN-WAIT-1状态。 2.服务端收到一个FIN(SEQx)标志的数据包,它…...

day54|110.字符串接龙, 105.有向图的完全可达性, 106.岛屿的周长
110.字符串接龙 110. 字符串接龙 (kamacoder.com) #include<iostream> #include<vector> #include<unordered_set> #include<unordered_map> #include<string> #include<queue>using namespace std;int main(){int n 0;cin >> n;…...

使用docker在CentOS 7上安装php+mysql+nginx环境教程并运行WordPress
文章目录 一、安装docker1、切换yum源并更新系统2、卸载旧版docker3、配置Docker的yum库4、安装Docker5、启动和校验Docker6、配置镜像加速6.1、注册阿里云账号6.2、开通镜像服务6.3、配置镜像加速二、部署php+mysql+nginx环境1、准备目录结构2、拉取镜像3、运行容器并从中拷贝…...

vite tsx项目的element plus集成 - 按需引入踩坑
前面我们进行了开源组件的自研,很多组件可直接用现成的开源组件库,并不需要自己重复造轮子,为此我们讲如何在当前vite vitepress tsx技术整合的项目中实现element plus组件的按需引入,同时解决遇到的一些坑。 安装Element Plus…...

Android GreenDao 升级 保留旧表数据
Android GreenDao 升级 保留旧表数据 大川的川关注IP属地: 北京 0.2052019.08.05 11:54:36字数 270阅读 363 瓦力和伊娃 GreenDao升级库版本号之后,以前的旧数据没有了,为啥,因为GreenDao在升级的时候会删除旧库,创建新库&#…...

记一次证书站有趣的SQL注入
一、确定站点 按照以前文章中提到的寻找可进站测试的思路,找到了某证书站的一处站点,通告栏中写明了初始密码的结构,因此我们可通过信息搜集进入该站点(可以考虑去搜集比较老的学号,因为这样的账号要么被冻结,要么就是…...

1_初识pytorch
之前完全没有了解过深度学习和pytorch,但现在因为某些原因不得不学了。不得不感叹,深度学习是真的火啊。纯小白,有错的欢迎指正~ 参考视频:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆…...

c++typeid()的使用
用处: typeid()函数主要用来获取对应类型或者变量的类型信息,其返回一个std::type_info的对象,这个对象中存放了对应类型的具体信息。 所以typeid()函数就是获取一个type_info的类型,然后可以通过此类型来获取到相应的类型信息。 type_info的…...

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十四)-租云服务器及配环境、docker基本命令
主要介绍了租云服务器和docker配置、基本命令!!! 文章目录 前言 一、云平台 二、租云服务器及安装docker 1.阿里云 2.安装docker 三、docker命令 将当前用户添加到docker用户组 镜像(images) 容器(container) 四、实战…...

实现一个全栈模糊搜索匹配的功能
提供一个全栈实现的方案,包括 Vue 3 前端、Express 后端和 MySQL 数据库的分类模糊搜索功能。让我们逐步来看: 1. 数据库设计 (MySQL) 首先,我们需要一个存储分类的表: CREATE TABLE categories (id INT AUTO_INCREMENT PRIMAR…...

智慧景区导览系统小程序开发
智慧景区导览系统小程序的开发是一个综合性的过程,旨在通过先进的技术手段提升游客的游览体验。以下是开发智慧景区导览系统小程序的主要步骤和关键点: 一、需求分析 市场调研:了解旅游市场的最新趋势和游客的实际需求,包括游客…...

HIVE调优方式及原因
3.HIVE 调优: 需要调优的几个方面: 1.HIVE语句执行不了 2.HIVE查询语句,在集群中执行时,数据无法落地 HIVE执行时,一开始语句检查没有问题,生成了多个JOB, …...

deploy local llm ragflow
CPU > 4 cores RAM > 16 GB Disk > 50 GB Docker > 24.0.0 & Docker Compose > v2.26.1 下载docker: 官方下载方式:https://docs.docker.com/desktop/install/ubuntu/ 其中 DEB package需要手动下载并传输到服务器 国内下载方式&…...

测桃花运(算姻缘)的网站系统源码
简介: 站长安装本源码后只要有人在线测算,就可以获得收入哦。是目前市面上最火的变现利器。 本版本无后台,无数据。本版本为开发的逗号联盟接口版本。直接对接逗号联盟,修改ID就可以直接运营收费赚钱。 安装环境:PH…...

电商平台优惠券
优惠券业务逻辑 优惠券的发放: 来源:优惠券可以由平台统一发放,也可以由商家自行发放。平台优惠券的优惠由平台承担,而店铺优惠券则由商家承担。类型:优惠券可以分为满减优惠券、无门槛优惠券等,根据使用限…...

内衣洗衣机多维度测评对比,了解觉飞、希亦、鲸立哪款内衣洗衣机更好
想要代替手洗内衣物,那么一台内衣专用的小型洗衣机就必不可少啦,不仅能够为我们节约更多的时间以及精力,还能大大提高内衣物的卫生,面对于市面上各种各样的小型内衣洗衣机,相信很多小伙伴都无从下手! 为一…...

数据结构和算法入门
1.了解数据结构和算法 1.1 二分查找 二分查找(Binary Search)是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半,然后比较目标值与中间元素的大小关系,从而确定应该在左半部分还是右半部分继续查找。这个…...

基于OpenCV C++的网络实时视频流传输——Windows下使用TCP/IP编程原理
1.TCP/IP编程 1.1 概念 IP 是英文 Internet Protocol (网络之间互连的协议)的缩写,也就是为计算机网络相互连接进行通信而设计的协议。任一系统,只要遵守 IP协议就可以与因特网互连互通。 所谓IP地址就是给每个遵循tcp/ip协议连…...

(BS ISO 11898-1:2015)CAN_FD 总线协议详解6- PL(物理层)规定3
目录 6.4 AUI 规范 6.4.1 一般规定 6.4.2 PCS 到 PMA 消息 6.4.2.1 输出消息 6.4.2.2 Bus_off 消息 6.4.2.3 Bus_off 释放消息 6.4.2.4 FD_Transmit 消息 6.4.2.5 FD_Receive 消息 6.4.3 PMA 到 PCS 消息 6.4.3.1 输入消息 如果有不懂的问题可在评论区点赞后留言&…...

docker环境下php安装扩展步骤 以mysqli为例
docker环境下php安装扩展步骤 以mysqli为例 1.0 前言2.0 php 扩展安装原理3.0 docker 环境下 php 扩展安装3.1 docker php 容器扩展安装路径及原理3.2 docker php 扩展脚本安装过程 同步发布在个人笔记[docker环境下php安装扩展步骤 以mysqli为例]( https://blog.lichenrobo.co…...

医院综合绩效核算系统,绩效核算系统源码,采用springboot+avue+MySQL技术开发,可适应医院多种绩效核算方式。
一、系统概述 作为医院用综合绩效核算系统,系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可…...

ROOM数据快速入门
ROOM数据库快速入门 文章目录 ROOM数据库快速入门第一章 准备工作第01节 引入库第02节 布局文件第03节 activity类第04节 效果图 第二章 数据类第01节 实体类(表)第02节 数据访问类(DAO)第03节 数据Service层第04节 RoomDataBase …...

刷新,前面接口的返回值没有到,第二个接口已经请求完了,导致第二个接口返回数据错误
刷新,前面接口的返回值没有到,(前端)第二个接口已经请求完了(入参没有拿前面那个接口返回的数据),导致第二个接口返回数据错误...

pdcj设计
为了实现这些功能需求,我们需要设计多个数据库表来存储相关的数据,并编写相应的Java代码来处理业务逻辑。下面是各个功能需求对应的MySQL表结构以及部分Java代码示例。 商品设置管理 商品分类管理 商品分类表 (product_categories)CREATE TABLE produ…...

【数据结构】哈希表的模拟实现
文章目录 1. 哈希的概念2. 哈希表与哈希函数2.1 哈希冲突2.2 哈希函数2.3 哈希冲突的解决2.3.1 闭散列(线性探测)2.3.2 闭散列的实现2.3.3 开散列(哈希桶)2.3.4 开散列的实现 2.4 开散列与闭散列比较 1. 哈希的概念 在我们之前所接触到的所有的数据结构…...

面试经典算法150题系列-数组/字符串操作之多数元素
序言:今天是第五题啦,前面四题的解法还清楚吗?可以到面试算法题系列150题专栏 进行复习呀。 温故而知新,可以为师矣!加油,未来的技术大牛们。 多数元素 给定一个大小为 n 的数组 nums ,返回其…...

海南云亿商务咨询有限公司领航抖音电商服务
在当下这个瞬息万变的互联网时代,短视频平台尤其是抖音,正以惊人的速度重塑着消费者的购物习惯与商家的营销版图。在这场电商盛宴中,海南云亿商务咨询有限公司凭借其在抖音电商领域的深厚积累与前瞻视野,正逐步成为众多商家转型升…...

C#初级——继承
继承 继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类(父类࿰…...

Github 2024-07-29 开源项目日报 Top10
根据Github Trendings的统计,今日(2024-07-29统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量JavaScript项目3非开发语言项目3Python项目1TypeScript项目1C++项目1Lean项目1HTML项目1免费编程学习平台:freeCodeCamp.org 创建周期:3302 天…...