JAVA反射机制分析-------spring的通过反射创建bean实例对象以及属性注入的原理解析

article/2025/10/10 0:54:53

                                                 JAVA反射机制

java反射机制是在运行状态中,对于任意一个类, 能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制。

巧妙的利用java中的反射机制,能够帮助我们进行程序开发时达到意想不到的效果,本片博文将对java中的反射机制进行分析以及针对spring中通过配置文件完成bean实例对象的创建原理进行分析。

一:java中获取类对象以及通过类对象获取实例化对象,并获取/修改类属性,方法。

二:通过读取自定义配置文件,完成对象的创建以及调用

三:模仿spring创建beanFactory的实现过程。

一:类对象

1. 什么是类对象,java的反射机制是什么意思?如何获取类对象

java的反射机制,意思很简单,就是通过类对象获取类的信息。

那什么是类对象呢?每一个类型的类都有且仅有一个类对象Class,该类对象中记录了这个类包含了哪些属性、哪些方法、以及有哪些构造方法等信息。那通过类对象,就可以反向获取该类的构造方法,就可以完成实例化,也可以访问类中的方法和属性。完成赋值、调用方法等操作。那类对象如何获取呢?很简单,只需要知道他的类名即可。

获取类对象的三种方法:

①Class a = Class.forName(pojo.Hero);

②Class b = Hero.class;

③Class c = new Hero().getClass;

并且a=b=c,因为在同一个JVM中,或者说在一个ClassLoader中,Hero这个类,有且只有一个类对象。

注意,在获取类对象时,除了Hero.class这种方式,其他的会导致类的属性被初始化,即该类中的静态代码块执行或者静态属性被初始化,可以理解为加载类时完成的事情,并且该初始化过程只会被执行一次。例如在Hero这个类中,添加static静态代码块,控制台输出"初始化",则通过三种方式获取类对象,类中的代码块会被执行一次,并在控制台打印了"初始化"。

2. 通过类对象创建实例化对象、访问属性、访问方法。

㈠通过类对象创建实例化对象:直接看代码,一个类对象可以通过构造方法创建无数个实例化对象。

    try {//Class h = Hero.class;//Class h = Class.forName("test.pojo.Hero");Class h = new Hero().getClass(); //获取类对象Constructor c = h.getConstructor(); //根据类对象获取默认构造器Hero h1 = (Hero)c.newInstance();  //通过构造器调用构造方法完成实例化h1.showName();  //通过实例化好的对象即可访问该类中的方法} catch (Exception e) {e.printStackTrace();}

㈡通过类对象访问类属性,修改类属性值

getField和getDeclaredField的区别
这两个方法都是用于获取字段
getField 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))

        Hero h = new Hero();h.setPrice("qq");  System.out.println(h.getPrice());Field f = h.getClass().getDeclaredField("price"); //根据类对象获取该类的price属性f.set(h, "tt"); //通过获取的属性,给h对象修改这个属性的值System.out.println(h.getPrice());

㈢通过类对象访问类的方法,并执行该方法。

        Hero h = new Hero();Hero h1 = new Hero();Method m = Class.forName("test.pojo.Hero").getMethod("setPrice", String.class);//通过类对象获取该类中的方法以及该方法中需要传入的参数对象
//如果没有参数的方法则不需要写第二个参数System.out.println(h.getPrice());m.invoke(h, "访问方法"); //通过m对象,访问h对象的setPrice,并传入参数值。此时便对h对象执行了该方法。m.invoke(h1, "h1");System.out.println(h.getPrice());System.out.println(h1.getPrice());

至此,已经完成了通过类对象调用构造方法、调用方法、修改属性值操作了。

接下来,将演示java的反射机制如何使用如何利用反射,以及在哪些场景中使用。

二:读取配置文件

1. 为什么要使用反射?如何利用反射机制

假设有一个场景,有两个不同的类根据业务需要可能需要随时调换,如果通过new的方式,则每次调换时需要修改代码。如果通过反射来完成,我们可以将类全名写在配置文件中,通过IO读取配置文件来决定实例化哪个对象。我们就不需要修改代码了,直接修改配置文件中的类名即可。这只是个小例子,JAVA的反射机制远比这要强大。下面通过读取配置文件的方式完成调用不同的类中的不同方法。

2. 编写配置文件,如下,配置文件中声明需要创建的类名称、需要调用的方法名、参数名、以及该参数的值。

通过读取该配置文件,完全利用反射来完成对象的创建和属性的注入。

①创建两个类文件,service、service1,在这两个类中分别写两个打印的方法,代表两种不同的业务,如果需要创建的是service1的话,则程序需要自动把读取到的name值注入到该对象的name属性中。

public class Service {public void show(String s) {System.out.println("this is show1"+s);}
}
public class Service1 {public String name;	public void show(String s) {System.out.println("this is show2"+s);}	public String getName() {return name;}public void setName(String name) {this.name = name;}	
}

②在项目中创建配置文件context.txt文件:

class=example.pojo.Service1
method=show
property=name
name=zhangSan

③编写一个方法,用来读取配置文件,并创建对象。输出需要调用的方法。如果是service1的话,则返回一个对象,该对象中已经被注入好了name值。

public Object getObj() throws Exception {File f = new File("F:\\project\\fansheORzhujie\\fanshe\\src\\example\\context.txt"); //读取配置文件Properties springConfig = new Properties(); springConfig.load(new FileInputStream(f)); //加载配置文件String className1 = (String) springConfig.get("class"); //获取配置文件中class的值String method1 = (String) springConfig.get("method"); //获取配置文件中method的值String pro = (String) springConfig.get("property");  //获取配置文件中的属性名String value = (String) springConfig.get("name");   //获取配置文件中name的属性值//根据读取的配置文件创建类对象Class c1 = Class.forName(className1);  //根据类对象创建类实例Constructor ct1 = c1.getConstructor();//实例化类对象Object o1 = ct1.newInstance();//根据反射读取类对象的方法名,然后具体某个实例化对象执行方法Method m1 = c1.getMethod(method1, String.class);m1.invoke(o1, "我是类对象中的方法"); //给这个方法注入参数//根据配置文件的创建,本例通过反射,读取配置文件中的信息,也完成创建对象(反转控制)和属性的依赖注入//如果类对象时service1,则判断,获取到该类中的name属性,并将实例化的对象中的name值设置为配置文件中name的值。if(c1 == Service1.class) {Field f1 = c1.getDeclaredField(pro);f1.set(o1, value);}return o1;}

④编写测试类:

	public static void main(String[] args) throws Exception {Test t = new Test();Object obj = t.getObj();if(obj.getClass() == Service1.class) {Service1 s = (Service1) obj;System.out.println(s.getName());}}

⑤输出结果:

⑥这样通过配置文件的方式,如果此时需要切换创建的对象,则只需要修改配置文件中的class值,修改类名,或者需要调用别的方法,则修改方法名。从而达到通过配置文件调控实例化哪个类的对象。或者,通过配置文件实例化一个对象,并给该对象注入参数值的实现。

spring中的bean的创建就是通过java的反射机制来完成的,基本实现过程与本例类似,spring实现时,是通过不断的读取配置文件,将class值,属性名,属性值,实例化成一个一个的object对象,然后将这些对象放到一个map中,key值就是配置文件中的id值或者name值,也就是形成了所谓的IOC容器beanFactory,使用时,直接通过key值获取,然后强转成需要的类型即可直接使用spring帮我们创建好的实例化对象了。接下来我将仿照spring中beanFactory的实现过程进行分析。

 

三:模拟spring创建beanFactory的实现过程

1. 简介

通过spring的配置文件,配置bean,使用spring类文件实例化bean中配置的对象,在测试类中直接返回使用。本demo中包含一个配置文件:spring.xml、一个spring类文件:applicationContext.java、一个实体类:product.java、一个测试类:text.java。

2.创建实体类与配置文件spring.xml,放在src路径下,设置好bean的属性。

package beans;
public class Product {public void show() {System.out.println("我是实例化对象product");}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans><bean id="p" class="beans.Product">		</bean>	<!-- <bean id="h" class="beans.Hero"></bean> -->
</beans>

3. 接下来我们自己创建一个bean工厂,用来生产实例化对象配置文件中配置好的bean,从而达到spring完成创建对象的目的。

①先读取到配置文件spring.xml,并创建一个存放bean的私有静态全局变量map集合。

②然后对该文件进行解析,获取到文件中的跟标签。也就是最外面的那层标签beans,需要导入dom4j-1.6.jar、xml-apis-1.0.b2.jar资源文件

循环跟标签beans中的bean标签,将每一个bean中的属性值获取出来,根据class属性通过反射创建类对象并完成实例化,然后将这个对象放在map中,key值是读取到的id的值,value值是实例化好的object对象。配置文件中有多少bean标签就会循环创建多少个对象。

④重写该类的构造方法,并传入参数,参数值为配置文件的路径文件名。

⑤编写getBean方法,传入参数值,参数为bean中的id,从而达到通过调用该方法,传入配置的id值,获取到bean工厂中的实例化对象。此方法也是提供给外部访问map的唯一入口。

请看代码:
 

package spring;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/*** spring的配置工厂类文件* @author lemon**/
public class ApplicationContext {private static Map<String, Object> beans = new HashMap<String, Object>();public ApplicationContext(String file) {buildBeanFactory(file);}public void buildBeanFactory(String file) {//读取配置文件	InputStream in = ApplicationContext.class.getClassLoader().//类的加载容器,把类的实例加载到内存中getResourceAsStream(file);//这个方法直接从src下读取该文件//解析文件//创建解析器对象SAXReader saxReader = new SAXReader();Document dom = null;try {dom = saxReader.read(in);} catch (DocumentException e) {e.printStackTrace();throw new RuntimeException("解析文件时抛出异常");}//解析完了该文件,接下来需要拿到根元素(bean元素)Element root = dom.getRootElement();//获取根元素所有的bean元素,并存在放list中,并且将所有的bean标签循环读取,把每一个bean都创建成一个实例化对象//然后存放在map中,此时,即完成了spring创建bean的过程。List<Element> list = root.elements("bean");for (Element e : list) {String className = e.attributeValue("class");String idName = e.attributeValue("id");Object obj = null;try {obj = Class.forName(className).newInstance();} catch (Exception e1) {e1.printStackTrace();throw new RuntimeException("创建对象时抛出了异常");}beans.put(idName, obj);}}/** 提供方法,以供外部应用访问并获取map中的值。也就是getBean方法*/public Object getBean(String id) {return beans.get(id);}/**提供方法,通过调用清初始化spring工厂中的所有值*/public void cleanBeans() {beans.clear();beans = null;}
}

4. 编写测试类:

创建spring类,传入配置文件名,调用getBean方法,类型转换至具体类型对象,直接调用对象中的属性和方法即可。

public class Test {public static void main(String[] args) {ApplicationContext app = new ApplicationContext("spring.xml");Product pro = (Product) app.getBean("p");System.out.println(pro);pro.show();}
}

5. 输出结果:

6. 总结:

spring在为我们使用时,内部源码异常复杂,此过程只不过是简化了的实现过程,实则有很多情况需要处理。

在tomcat中使用spring时,tomcat是如何加载spring的?

其实在tomcat的启动过程中,会加载web.xml文件,我们在该文件中配置了监听上下文启动的监听器。在tomcat启动时,该监听器会被加载,然后执行该类中的初始化方法。在初始化时,默认读取默认路径下的默认spring配置文件名applicationContext.xml文件,并且开始加载解析,根据反射机制创建了配置文件中bean的实例化对象,并存在在map集合中,并完成加载其他资源文件等过程。从而spring就跟随者tomcat的启动完成了加载。

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

对于spring的世界,还有许许多多的未知等待我们去探索,在我以后的学习时间里,我会更专注于spring原理的解读。

 

 

 

 

 


http://chatgpt.dhexx.cn/article/E6OmcJiC.shtml

相关文章

【Spring】spring的反射机制详解

一、什么是反射&#xff1a; &#xff08;1&#xff09;Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得到class对象之后&#xff0c;再通过class对象进行反编译&#xff0c;从而获取对象的各种信息。 &…

Spring————java的反射机制,Spring的IOC和DI

一、认识Spring 1.1、Spring家族 SpringFramework&#xff1a; Spring框架&#xff1a;是Spring中最早核心的技术&#xff0c;也是所有其他技术及的基础。 SpringBoot:Spring是用来简化开发。而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。 SpringCloud&#xf…

分布式定时任务调度实战

目录 1、为什么需要定时任务 2、定时任务调度框架 2.1 单机 2.2 分布 3、xxl-job和elastic-job对比 3.1 支持集群部署方式 3.2 多节点部署任务执行方式 3.3 日志可追溯 3.4 监控告警 3.5 弹性扩容缩容 3.6 支持并行调度 3.7 高可用策略 3.8 失败处理策略 3.9 动态…

浅谈传统定时任务和分布式定时任务

为什么用定时任务&#xff1f; 定时任务平台可以在后台自动检测数据并进行操作。主要应用在订单状态改变、后台统计、定时发送邮件或短信等。 定时任务怎么部署实现&#xff1f; 传统的定时任务可以通过可定时线程池、timertask、quartz、spring-schedule方式来进行处理。他…

分布式定时任务技术选型

1、目前的定时任务方案 Java中开发大多数使用Spring-Scheduler&#xff0c;只需要在Spring中的bean的对应方法加上sheduler注解即可完成我们的定时任务&#xff0c;但是光是用这个注解还远远不能保证定时任务执行多次&#xff0c;我们需要一些其他手段的保证&#xff0c;一般来…

java 分布式 定时任务_Java中实现分布式定时任务的方法

定时器Scheduler在平时使用比较频繁&#xff0c;在springboot中&#xff0c;配置好Scheduled和EnableScheduling之后&#xff0c;定时器就能正常执行&#xff0c;实现定时任务的功能。 但是在这样的情况下&#xff1a;如果开发的服务需要水平部署实现负载均衡&#xff0c;那么定…

【手把手】分布式定时任务调度解析之Quartz

1、任务调度背景 在业务系统中有很多这样的场景&#xff1a; 1、账单日或者还款日上午 10 点&#xff0c;给每个信用卡客户发送账单通知&#xff0c;还款通知。如何判断客户的账单日、还款日&#xff0c;完成通知的发送&#xff1f; 2、银行业务系统&#xff0c;夜间要完成跑批…

轻量级分布式定时任务框架XXL-Job

轻量级分布式定时任务框架XXL-Job: XXL-JOB是一款轻量级的分布式定时任务框架&#xff0c;上手简单&#xff0c;操作容易&#xff0c;XXL-Job可以到官网下载也可以去gitee上拉取源码&#xff0c;其中核心模块分页两个&#xff1a;1&#xff1a;是分布式调度服务&#xff0c; 2&…

Springboot结合Redis实现分布式定时任务

一、背景 之前分享过分布式定时任务的技术选型方案&#xff1a;分布式定时任务技术选型方案&#xff0c;个人青睐xxl_job&#xff0c;分享了搭建接入流程&#xff1a;xxl_job搭建方案&#xff0c;本次项目需求较为简单&#xff0c;同时时间紧张。下面介绍利用Redis锁实现分布式…

分布式定时任务框架说明

分布式定时任务框架说明 分布式定时任务框架说明Quartz概念架构组件springboot集成方式使用内存使用数据库 TBSchedule&#xff1a;elastic-job概念架构组件执行流程特性 satumxxl-job概念特性架构组件使用 分布式定时任务框架说明 Quartz 概念 Quartz&#xff1a;Java事实上…

分布式定时任务对比

1. 什么是分布式定时任务 把分散的&#xff0c;可靠性差的计划任务纳入统一的平台&#xff0c;并实现集群管理调度和分布式部署的一种定时任务的管理方式。叫做分布式定时任务。 2. 常见开源方案 elastic-job , xxl-job &#xff0c;quartz , saturn, opencron , antares el…

简单粗暴的分布式定时任务解决方案

分布式定时任务 1.为什么需要定时任务&#xff1f;2.数据库实现分布式定时任务3.基于redis实现 1.为什么需要定时任务&#xff1f; 因为有时候我们需要定时的执行一些操作&#xff0c;比如业务中产生的一些临时文件&#xff0c;临时文件不能立即删除&#xff0c;因为不清楚用户…

Java 实现分布式定时任务

文章目录 前言一、技术点二、代码实践1、引入库2、创建启动线程入口3、表结构4、任务解析5、任务拉取 三、结果展示四、总结 前言 最近有一个需求&#xff1a;需要实现分布式定时任务。而市面上的定时任务大多数都是基于Scheduled注解进行实现。不符合需求。所以根据需求整体思…

分布式定时任务调度

前言 什么是分布式定时任务? 把分散的&#xff0c;可靠性差的计划任务纳入统一的平台&#xff0c;并实现集群管理调度和分布式部署的一种定时任务的管理方式。叫做分布式定时任务。 为什么要采用分布式定时任务&#xff1f; 单点定时任务的缺点: 功能相对简单&#xff0c…

分布式定时任务-XXL-JOB-教程+实战

一.定时任务概述 1.定时任务认识 1.1.什么是定时任务 定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行&#xff0c;或者周期性的去执行某个任务&#xff0c;比如&#xff1a;每天晚上24点做数据汇总&#xff0c;定时发送短信等。 1.2.常见定时任务方案…

几种常用的分布式定时任务

1. 什么是分布式定时任务 把分散的&#xff0c;可靠性差的计划任务纳入统一的平台&#xff0c;并实现集群管理调度和分布式部署的一种定时任务的管理方式。叫做分布式定时任务。 2. 常见开源方案 elastic-job xxl-job quartz saturn opencron antares elastic-job el…

分布式定时任务

分布式定时任务 1&#xff0c;什么是分布式定时任务&#xff1b;2&#xff0c;为什么要采用分布式定时任务&#xff1b;3&#xff0c;怎么样设计实现一个分布式定时任务&#xff1b;4&#xff0c;当前比较流行的分布式定时任务框架&#xff1b; 1&#xff0c;什么是分布式定时…

python类型转换函数str

str函数&#xff0c;将数字转为字符串&#xff1a;

Python 类型转换(数据类型转换函数大全)

文章目录 虽然 Python 是弱类型编程语言&#xff0c;不需要像 Java 或 C 语言那样还要在使用变量前声明变量的类型&#xff0c;但在一些特定场景中&#xff0c;仍然需要用到类型转换。 比如说&#xff0c;我们想通过使用 print() 函数输出信息“您的身高&#xff1a;”以及浮点…

python怎么转换文件格式_python怎么转换数据类型

在处理数据的时候&#xff0c;经常需要转换数据的格式&#xff0c;来方便数据遍历等操作。下面我们来看一下Python中的几种数据类型转换。 1、字符串转字典&#xff1a;dict_string "{name:linux,age:18}" to_dict eval(dict_string) print(type(to_dict)) 也可以用…