Java反射角度简单理解spring IOC容器

概述

Java反射(Reflection)是Java编程语言的一个特性,它允许在运行时对类、接口、字段和方法进行动态查询和操作。反射提供了一种在运行时查看和修改程序行为的能力,这通常用于实现一些高级功能,如框架(Spring)、ORM(对象关系映射)工具、IDE(集成开发环境)等。 
反射的主要用途
动态加载类:可以在运行时加载和使用类,而无需在编写/编译时知道它们。
检查类信息:可以获取类的名称、父类、实现的接口、字段、方法等。
动态调用方法:可以调用类的任何公共方法,即使该方法在编译时未知。
创建和操作对象:可以创建类的实例,并调用其方法或访问其字段。
修改字段值:可以修改类的私有字段的值(但通常不推荐这样做,因为它破坏了封装性,又名暴力反射)。 

反射使用场景及理解 

1.1. 动态加载类信息及动态调用类中的方法

 假设我们需要报考微软云的证书证书分别有如下几种,而且将来可能还会新增。
Azure900, Azure903, Azure300, Azure380

我们假设有这样一个需求,当用户传入的参数能模糊匹配上如下的方法名时,就算要报名该考试。如我传入 "30", 这个值正好能模糊匹配上方法名“singUpAzure930Exam”和“signUpAzure300Exam”,这时就报名930 和300两门考试,如我传入Azure,很显然,四门课程都会报名。具体需要传入什么样的参数根据用户需求动态而定。

我们这里用伪代码编写一个报名系统,首先我们申明一个报名类。

public class AzureCloudExam {public void AzureCloudExam(){}public String signUpAzure900Exam(){return "You signed up Azure 900!";}public String singUpAzure930Exam(){return "You signed up Azure 930!";}public String signUpAzure300Exam(){return "You signed up Azure 300!";}public String signUpAzure380Exam(){return "You signed up Azure 380!";}
}

显然这种场景,使用我们以往的先new 对象,再调用方法的普通模式是很难做到的,而且将来有新增加的考试方法后,我们也需要修改调用考试方法的服务类才能做到,继而需要修改服务代码,重新部署等麻烦。因为这种普通模式需要调用哪个方法是我们编写是就必须按照逻辑提前写好的,显然它不够灵活,然后在我们平时的开发过程中,普通方式new 对象调用方法已经足够用了,但是在面临一些更加灵活的需要时,就不行了,如spring框架等都有大量的使用反射解决,因为我们在使用spring框架开发时,显然编写阶段spring也是不知道需要加载哪些bean的。而是运行时根据注解动态生成的。

因而如果我们使用反射,就可以很好的解决动态调用方法的这个问题,模拟步骤如下:

编写服务代码用于调用如上的考试类。

package com.mycompany.myreflect.refDemo2;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class MyReflectionServer {public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {//接收用户传入参数String className = args[0];String examLike = args[1];//动态加载需要使用class字节码文件Class<?> clazz = Class.forName(className);//动态无参构造方法new 对象Object obj = clazz.getDeclaredConstructor().newInstance();//获取class字节码中对应的所有方法Method[] allMethods = clazz.getDeclaredMethods();//loop所有方法for (Method method:allMethods){//如果用户传入的参数能模糊匹配上当前方法名if (method.getName().contains(examLike)){//调用报名方法String result = (String) method.invoke(obj);System.out.println(result);}}}
}

如我们传入参数
运行结果

如上实现了动态决定调用哪个方法。
如将来还要加入Alicloud 的考试门类,我们也只需要添加Alicloud的报名类就可以了,而服务类的代码不需要做任何改动。这就是反射带来的动态调用方法的灵活性。

1.2. 动态创建类对象

假设我们有这样一个需求。还是如上的报名考试系统,但是由于业务的扩展,现在不仅要支持微软云认证报名,还需要支持阿里云报名。将来还会新增厂家。报名的用户也在爆发增长。
显然我们每次客户请求都new一个对应厂商的的类,然后再在该类中去调用对应厂商的服务接口是报名,会带来不必要的对象创建和销毁。我们这里只需要一个单例的bean对象就可以了。

  • 在创建报名类之前我们先创建一个注解@MySupportExam, 用于注解某厂商的某类考试我们是否支持在我们这里报名,因为有的门类报名,对应厂商有授权考虑。
    package com.mycompany.myreflect.refDemo2;import java.lang.annotation.*;@Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MySupportExam {
    }
    
  • 创建@MyComponent注解,用于标准这是我们需要注入的厂商bean
    package com.mycompany.myreflect.refDemo2;import java.lang.annotation.*;@Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyComponent {
    }
  • 创建对应厂商的报名类。
    package com.mycompany.myreflect.refDemo2;@MyComponent
    public class AzureCloudExam {public void AzureCloudExam(){}@MyEnableSupportpublic String signUpAzure900Exam(){return "You signed up Azure 900!";}@MyEnableSupportpublic String singUpAzure930Exam(){return "You signed up Azure 930!";}public String signUpAzure300Exam(){return "You signed up Azure 300!";}public String signUpAzure380Exam(){return "You signed up Azure 380!";}
    }
    
    package com.mycompany.myreflect.refDemo2;@MyComponent
    public class AliCloudExam {public void AliCloudExam(){}public String signUpAli900Exam(){return "You signed up Ali 900!";}@MyEnableSupportpublic String singUpAli930Exam(){return "You signed up Ali 930!";}@MyEnableSupportpublic String signUpAli300Exam(){return "You signed up Ali 300!";}public String signUpAli380Exam(){return "You signed up Ali 380!";}
    }
    
  •  创建一个ClassScanner 扫描添加了@MyCompany注解的厂商类
    package com.mycompany.myreflect.refDemo2;
    import java.io.File;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.Enumeration;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Set;public class ClassScanner {public static Set<Class<?>> scanPackage(String packageName) {Set<Class<?>> classSet = new LinkedHashSet<>();try {Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replace(".", "/"));while (urls.hasMoreElements()) {URL url = urls.nextElement();String protocol = url.getProtocol();if ("file".equals(protocol)) {String filePath = URLDecoder.decode(url.getPath(), "UTF-8");findClassByPackageName(new File(filePath), packageName, classSet);}}} catch (Exception e) {e.printStackTrace();}return classSet;}private static void findClassByPackageName(File file, String packageName, Set<Class<?>> classSet) throws ClassNotFoundException {if (file.isFile() && file.getName().endsWith(".class")) {String className = file.getName().substring(0, file.getName().length() - 6);String fullClassName = packageName + "." + className;Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(fullClassName);classSet.add(clazz);} else if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {findClassByPackageName(f, packageName, classSet);}}}public static List<Class<?>> getExamCompanyClazz(List<Class<?>> examCompany,String packageName) {Set<Class<?>> classes = scanPackage(packageName);for (Class<?> clazz : classes) {if (clazz.getDeclaredAnnotation(MyComponent.class) !=null){examCompany.add(clazz);}}return examCompany;}
    }
    
  •  创建一个容器类用于管理所有的厂商bean,和方法bean
    package com.mycompany.myreflect.refDemo2;import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;public class ExamContainer {private Map<Class<?>,List<Method>> methodsSupportMap;private Map<Class<?>,Object> servicesMap;private List<Class<?>> examCompanies;public void init() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {examCompanies = new ArrayList<>();servicesMap = new HashMap<>();methodsSupportMap = new HashMap<>();ClassScanner.getExamCompanyClazz(examCompanies,"com.mycompany.myreflect.refDemo2");for (int i = 0;i<examCompanies.size();i++){Class<?> clazz = examCompanies.get(i);//将获取到的厂商字节码,判断容器中是否存在该对象,如果不存在则创建该对象并放入容器中setServiceInstanceByClass(clazz);setMethod(clazz);}}public void setServiceInstanceByClass(Class<?> clazz) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {Object obj = clazz.getDeclaredConstructor().newInstance();servicesMap.put(clazz,obj);}public void setMethod(Class<?> clazz){Method[] methods =clazz.getDeclaredMethods();// 如果方法加了@MySupportExam注解,说明我们这里支持该科目考试,加入容器for (Method method: methods){if (method.getDeclaredAnnotation(MySupportExam.class) != null){if (methodsSupportMap.get(clazz)!=null){methodsSupportMap.get(clazz).add(method);}else {List<Method> methodList = new ArrayList<>();methodList.add(method);methodsSupportMap.put(clazz,methodList);}}}}public void signUpExam(String examLike) throws InvocationTargetException, IllegalAccessException {for (Class<?> clazz:examCompanies) {Object object = servicesMap.get(clazz);List<Method> methodList = methodsSupportMap.get(clazz);for (Method method : methodList) {if (method.getName().contains(examLike)) {System.out.println(method.invoke(object));}}}}}
    
  • 用户请求服务方法调用 
    package com.mycompany.myreflect.refDemo2;import java.lang.reflect.InvocationTargetException;public class MyReflectionServer {public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {ExamContainer examContainer = new ExamContainer();examContainer.init();String examLike = args[0];examContainer.signUpExam(examLike);}
    }
    

    请求参数
    运行结果

    请求参数

    运行结果

 1.3. 暴力反射

最近在开发过程中使用到暴力反射的一个案例就是,当我去给一个service类写UT实,发现某些方法是private的,但是这些private的方法,又是需要写UT的,这个时候我们就可以利用反射的暴力反射去为这个方法写UT调用。只需要设置如下属性即可,这里不在代码举案例。

​​​​​​​method.setAccessible(true);

注意事项

性能:反射通常比直接方法调用要慢得多,因为涉及到了额外的类型检查和安全性检查。因此,在性能敏感的代码中应谨慎使用。
安全性:反射允许绕过访问控制检查(例如,可以访问和修改私有字段),这可能导致安全问题。因此,在使用反射时应格外小心。
可读性:使用反射的代码通常比直接代码更难理解和维护。因此,在可以使用直接代码的情况下,应避免使用反射。 

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

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

相关文章

算法:树状数组

文章目录 面试题 10.10. 数字流的秩327. 区间和的个数315. 计算右侧小于当前元素的个数 树状数组可以理解一种数的存储格式。 面试题 10.10. 数字流的秩 假设你正在读取一串整数。每隔一段时间&#xff0c;你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。 请实现数据结构…

网络信息安全

目录 引言 网络信息安全的基本概念 定义 主要目标 网络信息安全的范围 主要威胁 恶意软件 黑客攻击 拒绝服务攻击 社交工程 内部威胁 常用技术和防护措施 加密技术 防火墙 入侵检测和防御系统 访问控制 多因素认证 安全审计和监控 安全培训和意识提升 未来发…

无人机支持下的自然灾害风险评估技术应用

张老师&#xff08;副教授&#xff09;&#xff0c;长期从事无人机遥感技术与应用&#xff0c;主持多项国家级科研项目&#xff0c;编写著作2部&#xff0c;第一作者发表科研论文20余篇。对无人机遥感的多平台、多传感应用现状以及涉及的核心技术具有很深的理解&#xff0c;精通…

失业潮中如何突围?优秀PPT案例助你职场逆袭

在这个变幻莫测的时代&#xff0c;失业潮像一场突如其来的暴风雨&#xff0c;许多人在职场的大海中迷失方向。但别担心&#xff0c;即使风浪再大&#xff0c;总有勇敢的航海者能够乘风破浪&#xff0c;找到属于自己的那片新大陆。 今天&#xff0c;我们就来聊聊&#xff0c;在…

MyBatis 学习笔记(一)

MyBatis 封装 JDBC :连接、访问、操作数据库中的数据 MyBatis 是一个持久层框架。 MyBatis 提供的持久层框架包括 SQLMaps 和 Data Access Objects&#xff08;DAO&#xff09; SQLMaps&#xff1a;数据库中的数据和 Java数据的一个映射关系 封装 JDBC 的过程Data Access Ob…

C++入门:从C语言到C++的过渡(3)

目录 1.内联函数 1.1内联函数的定义 1.2特性 2.auto关键字 2.1auto的简介 2.2注意事项 3.范围for 4.nullptr空指针 1.内联函数 在C语言中&#xff0c;无论使用宏常量还是宏函数都容易出错&#xff0c;而且无法调试。而C为了弥补这一缺陷&#xff0c;引入了内联函数的概…

为了“降本增效”,我用AI 5天将SpringBoot迁移到了Nodejs

背景 大环境不好&#xff0c;各行各业都在流行“降本增效”&#xff0c;IT行业大肆执行“开猿节流”&#xff0c;一顿操作效果如何&#xff1f;普通搬砖人谁会在乎呢。 为了收紧我的口袋&#xff0c;决定从头学习NodejsTypeScript&#xff0c;来重写我的Java后端服务。 其实这…

浅谈面向对象--知识补充

This关键字 this 内存图 this关键字表示当前对象本身&#xff0c;一般用于类的内部&#xff0c;其内部存在一个地址&#xff0c;指向当前初始化的对象本身。 当new一个对象时&#xff0c;实际上产生了两个引用&#xff0c;一个是供类Dog内部调用其成员变量和成员方法的this关键…

kotlinx.coroutines.debug.AgentPremain

大家好 我是苏麟 . 项目引入AI大模型 debug 出现报错 设置 勾选

配置docker阿里云镜像地址

一、安装docker的步骤&#xff1a; 1.yum install -y yum-utils 2.yum-config-manager --add-repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker-ce.repo --配置阿里云仓库3.yum makecache fast4.yum install docker-ce -y5.docker version …

Vue3实战笔记(43)—Vue3组合式API下封装可复用ECharts图表组件

文章目录 前言一、封装echart图标钩子二、使用步骤总结 前言 接上文&#xff0c;已经安装好了ECharts&#xff0c;开始封装组件方便使用。 一、封装echart图标钩子 首先应用我们之前学习的钩子方式&#xff0c;在hooks目录下创建一个名为 useECharts.js 的文件&#xff0c;用…

【机器学习300问】97、机器学习中哪些是凸优化问题,哪些是非凸优化问题?

在机器学习的领域中&#xff0c;多数模型的参数估计问题实质上可以转化为优化问题。鉴于机器学习模型的多样性&#xff0c;不同的模型会对应着不同的损失函数&#xff0c;进而形成各具特色的优化问题。了解优化问题的形式和特点&#xff0c;对于提升我们求解模型参数的效率和准…

微信好友这样打标签更高效!

为什么要做标签管理? ① 通过标签管理&#xff0c;可以清晰的知道每个私域好友的关系程度&#xff0c;如哪些是忠诚客户&#xff0c;哪些是意向客户&#xff0c;哪些是刚加上的客户等等。 这样就知道下一步要怎么操作&#xff0c;做到精细化运营。如忠诚客户跟进维护&#x…

Embedding 模型的选择和微调

目录 引言 向量模型在 RAG 系统中的作用 1. 对 query 和 私域知识 进行向量化表示 2. 动态更新知识库 3. 数据隐私和安全 有哪些性能不错的向量模型 OpenAI Embedding JinaAI Embedding BAAI/bge Embedding 模型评测 MTEB 排行榜&#xff1a;https://huggingface.co…

第十六节:带你梳理Vue2: 生命周期与钩子函数

前沿: 通过前面几节的学习&#xff0c;我们已经对vue有了初步的了解&#xff0c;大致了解了vue可以帮我们干什么&#xff0c; 那么接下来我们就来看看vue的生命周期和它常用的钩子函数, 1. 理解生命周期的含义 生命周期&#xff1a;就是一个组件从实例化创建并添加到DOM树开…

读论文 | Small object detection model for UAV aerial image based on YOLOv7

目录 1、前言 2、摘要 3、论文的方法 3.1 方法描述 3.2 方法改进 3.3 本论文的模型图 3.4 本文的数据集&#xff1a; 3.5 论文实验 3.6 解决的问题 3.7 论文总结 &#xff08;1&#xff09;文章优点 &#xff08;2&#xff09;方法创新点 &#xff08;3&#xff0…

地质考察AR远程交互展示系统辅助老师日常授课

广东这片充满活力的土地&#xff0c;孕育了一家引领ARVR科技潮流的杰出企业——深圳华锐视点&#xff0c;作为一家专注于VR/AR技术研究与业务开发的先锋公司。多年来&#xff0c;我们不断突破技术壁垒&#xff0c;将AR增强现实技术与各行各业的实际需求完美结合&#xff0c;助力…

Cloneable接口和深拷贝

在java中如何对对象进行拷贝呢&#xff1f;我们可以使用Object类中的clone方法。 一、浅拷贝 在使用clone方法对对象进行拷贝的时候&#xff0c;需要注意&#xff1a; 1.需要重写clone方法&#xff1b; 2.clone方法的返回值是Object类&#xff0c;需要强制类型转化&#xf…

部门来了个测试开发,听说是00后,上来一顿操作给我看蒙了...

公司新来了个同事&#xff0c;听说大学是学的广告专业&#xff0c;因为喜欢IT行业就找了个培训班&#xff0c;后来在一家小公司实习半年&#xff0c;现在跳槽来我们公司。来了之后把现有项目的性能优化了一遍&#xff0c;服务器缩减一半&#xff0c;性能反而提升4倍&#xff01…

阅读笔记——《ProFuzzBench: A Benchmark for Stateful Protocol Fuzzing》

【参考文献】Natella R, Pham V T. Profuzzbench: A benchmark for stateful protocol fuzzing[C]//Proceedings of the 30th ACM SIGSOFT international symposium on software testing and analysis. 2021: 662-665.【注】本文仅为作者个人学习笔记&#xff0c;如有冒犯&…