【spring源码系列-01】spring底层源码整体概述

news2024/11/25 22:44:19

JVM系列整体栏目


内容链接地址
【一】spring源码整体概述https://blog.csdn.net/zhenghuishengq/article/details/130940885

初识虚拟机与java虚拟机

  • 一,spring源码整体概述
    • 1,初步概述
    • 2,扩展点机制
    • 3,核心方法refresh
    • 4,BeanFactory和FactoryBean的区别

一,spring源码整体概述

1,初步概述

在分析这个源码之前,首先需要去官网下载这个spring的源码包,建议下载5.x.x的版本。我这里安装的是:https://github.com/spring-projects/spring-framework/tree/5.2.x

spring框架在我们的认知中,是一个框架,也是一个生态。在spring中,最主要的就是两部分:一个是IOC,另一个是AOP。 IOC由被称为控制反转,是一种思想,就是将对象交给容器管理,而与其对应的是DI,被称为依赖注入,是IOC的一种具体实现。AOP是在IOC基础的一个升华,因此需要透彻的了解IOC之后,再来了解AOP。

IOC一般是作为bean对象的容器,其构建对象的基本流程如下,中间省略部分扩展点,如一些后置处理器,增强器等,后序会一一补上。下面这张图主要是针对于注解的方式获取bean的流程图。
在这里插入图片描述

1,获取上下文一般会通过两种方式获取,一种是通过XML的方式获取,一种是通过注解的方式获取,XML在现在流行的如日中天的springboot中很少使用,因此这里也选择通过注解的方式获取这个上下文ApplicationContext。

public class MainClass {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(MainCofig.class);
		User user = (User)context.getBean("user");
		System.out.println("user的类型:"+user.getClass());
	}
}

@Configuration
@ComponentScan(basePackages = {"com.zhs.study"})
public class MainCofig {
}

当然,也可以通过这个XML的方式来获取上下文,在最初接触spring的时候,就是将bean卸载xml文件中的。

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("xxx.xml");

2,而不管是使用这个XML的配置文件还是使用这个注解的方式获取到上下文,他们在调用getBean之前,都得生成一个BeanDefinition的bean定义,有了具体的bean定义之后,才能通过这个bean工厂去生产或者获取这个bean对象。而在获取到一个统一的BeanDefinition之前,那么就需要通过实现这个BeanDefinitionReader接口来将不同的上下文配置信息转换成统一的BeanDefinition

public interface BeanDefinitionReader {...}

BeanDefinitionReader接口有着具体的实现类,如下图所示,有着上面所说的xml和注解生成bean定义的读取器等,相当于作为一个统一的解析器,

在这里插入图片描述

随后通过一个BeanDefinitionRegistry的注册器,将读取到的东西注册成一个BeanDefinition,这样就能统一的生成一个bean工厂可以识别的一个BeanDefinition对象了

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
	try {
		Document doc = doLoadDocument(inputSource, resource);
		return registerBeanDefinitions(doc, resource);
	}
}

由于生成的beanDefinition的数量比较多,因此会先将这个bean定义先加入到这个beanDefinitionMap里面。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

3,随后会调用这个getBean()方法,从这个bean工厂中获取值,如果这个bean定义存在则直接获取,不存在则先创建再将值返回。

Student student = (Student) context.getBean("student");

在获取这个bean的过程中,主要会经历实例化,属性填充和初始化三个主要的过程,分别是实例化、属性填充、初始化 这三个主要的步骤。实例化就是在堆内存中开辟一块空间,并且给对象的属性值赋予默认值;属性填充就是往这个对象中set最终的值;最后再执行init初始化方法。并且这三个步骤的顺序不能改变。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tqAi96hL-1685408227594)(img/1685340071844.png)]

在填充属性到这个初始化的过程中,可以设置一些Aware接口的属性,实现这些Aware接口可以获取上下文等。如某个对象想获取整个上下文的信息,就可以实现ApplicationContextAware,外部获取到这个实例之后,就可以直接通过getApplicationContext方法获取上下文。

//实现上下文的aware接口
@Component
public class First implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    //实现方法
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    //将值返回
    private ApplicationContext getApplicationContext(){
        return applicationContext;
    }
}

Aware的作用就是:当Spring容器创建的bean对象在进行具体操作的时候,如果需要容器中的其他对象,此时可以将对象实现这个Aware接口,从而满足当前的需要。

除了设置Aware,还可以设置一些BeanPostProcessor 处理器,首先会调用BeanPostProcessor.before()方法,最后会调用这个BeanPostProcessor.after() 方法,在这两个处理器中间,执行的就是上面说的init初始化方法。而这两个处理器的方法如下

//前置处理器
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}
//后置处理器
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}

4,随后生成一个完整的bean对象,存储在这个concurrentHashMap一级缓存中,后续直接在map中获取实例即可。这就是一个粗略的的ioc流程。

5,在spring容器中,又将spring bean对象分为普通对象和容器对象,容器对象相当于一个内置的对象,而普通对象相当于自定义对象。内置对象就是类似于一些需要提前加载的类,并没有显式的自定义一些对象,自定义对象就是一些自定义在xml中或者注解中的一些实例对象。

2,扩展点机制

在上述粗略的描述了一下IOC的整体流程,除了上面这些组件之外,还存在的一些扩展点机制,如一些PostProcessor 后置处理器,主要有BeanFactoryPostProcessor和BeanPostProcessor处理器,前者主要是用来增强beanDefinition信息的,后者可以用来增强bean信息的,如aop等。

BeanFactoryPostProcessor的接口信息如下,并且该接口是一个函数式接口,在spring容器中,该接口是以集合的形式存在,就是会有多个这种bean工厂的后置处理器。

//函数式接口
@FunctionalInterface
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanPostProcessor的接口信息如下,里面有两个方法,被称为后置处理器的前置方法和后置处理器的后置方法

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

如下面这段BeanFactoryPostProcessor的举例,在实现这个接口之后,重写里面的postProcessBeanFactory方法就可以获取到beanDefination,并且对这个bean定义做一个增强的功能。里面可以设置一些bean的描述,加一些描述信息,是否设置成类加载等等。

/**
 * 自定义bean工厂的后置处理器类
 * 对beanDefinition做增强功能
 * @author zhenghuisheng
 * @date : 2023/5/25
 */
@Component
public class BeanFactoryTest implements BeanFactoryPostProcessor {
    /**
     * @param beanFactory : beanDefinition
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("taskTestController");
        taskTestController.setDescription("hhhhha");
        System.out.println("=================taskTestController增强完成!");
        System.out.println(taskTestController.getDescription());
    }
}

3,核心方法refresh

除此之外,xml方式的构造方法和注解的构造方法里面,里面都有一个重要的方法refresh ,他们的构造方法如下

//XML的方式获取到上下文
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
	super(parent);   // 初始化父类 ,获得xml路径资源解析器
	setConfigLocations(configLocations);  // 通过环境变量解析 xml路径
	if (refresh) {
		refresh();   // 这个方法时spring是最终要的一个方法,甚至体系整个ioc的声明周期
	}
}

//注解的方式获取上下文
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	//调用构造函数
	this();
	//注册我们的配置类
	register(annotatedClasses);
	//IOC容器刷新接口
	refresh();
}

而在这个refresh方法中,可以说是整个spring底层的核心,其代码如下,因此后文的spring源码的继续分析,基本就是围绕下面的这些方法。

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		//1:准备刷新上下文环境
		prepareRefresh();
		//2:获取告诉子类初始化Bean工厂  不同工厂不同实现
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		//3:对bean工厂进行填充属性
		prepareBeanFactory(beanFactory);
		try {
			// 第四:留个子类去实现该接口,bean工厂的后置处理器
			postProcessBeanFactory(beanFactory);
			// 调用我们的bean工厂的后置处理器. 
       	 	//1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
			invokeBeanFactoryPostProcessors(beanFactory);
			// 注册我们bean的后置处理器
			registerBeanPostProcessors(beanFactory);
			// 初始化国际化资源处理器.
			initMessageSource();
			// 创建事件多播器
			initApplicationEventMulticaster();
			// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
			onRefresh();
			//把我们的事件监听器注册到多播器上
			registerListeners();
			// 实例化我们剩余的单实例bean.
			finishBeanFactoryInitialization(beanFactory);
			// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
			finishRefresh();
		}
    }
}

4,BeanFactory和FactoryBean的区别

BeanFactoy是一个大的容器接口,ApplicationContext是其具体的实现接口,主要用于创建和获取对象,使用这个BeanFacory的时候必须遵循完整的创建的过程,这个过程是由spring来控制管理的。

FactoryBean只需要调用getObject就可以返回具体的对象,整个对象的创建过程是由用户自己来控制的,更加灵活,也可以用来创建一些特殊的bean,更加复杂的bean。在这个FactoryBean对象中,主要有三个方法,分别是:IsSingleton、getObject、getObjectType 这三个方法,使用的最频繁的就是getObject这个方法。

preInstantiateSingletons 的这个方法中,有一段判断逻辑如下,判断这个bean是不是工厂bean

//是不是工厂bean
if (isFactoryBean(beanName)) {...}

如果不获取该对象,那么该对象具体的值是看不见的,因为该对象没有提前创建。接下来再看一段代码,一个实体类实现这个bean工厂,再创建一个老师对象和学生对象的实体类

/**
 * @author zhenghuisheng
 * @date : 2023/5/29
 */
@Component
public class TeacherFactory implements FactoryBean<Teacher> {
    @Override
    public Teacher getObject() throws Exception {
        return new Teacher();
    }

    @Override
    public Class<?> getObjectType() {
        return Teacher.class;
    }
}

接下来通过上下文获取这个工厂中的实体类,查看结果如下

//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");
//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");

也就是说可以通过这个FactoryBean返回的对象不是当前对象,也可以通过这个& 获取原来的值,可以使得获取值的方式更加的灵活。而使用这个FactoryBean的意义就是:只需要通过重写这个getObject方法就可以获取到对应的bean对象,而不需要完全遵守这个bean的生命周期

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

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

相关文章

【wpf】xaml 中的参数复用

背景 xaml中有几种复用的方式&#xff1a; 有时在xaml中&#xff0c;我们需要复用一些参数&#xff0c;比如 固定的一个值。 有时是固定的一个样式。 资源&#xff0c;sys的引入 有时多个控件都要设置一个高度&#xff0c;我可以引入sys 声明 我就使用这个吧&#xff1a…

扬帆出海正当时,企业应该做好哪些准备?

在跨境出海的时代大潮中&#xff0c;想要拓展海外市场的中国企业&#xff0c;应该事先做好哪些准备&#xff1f; 中国企业出海的新格局 首先来看一组令人振奋的数据。来自中国信通院的数据显示&#xff0c;在2020年的时候&#xff0c;中国数字经济的规模就达到了39.2万亿元人民…

本地Linux搭建web服务并发布公网访问

文章目录 前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar内网穿透3.2 创建隧道3.3 测试公网访问 4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名4.3 测试访问公网固定二级子域名 转载自cpolar极点云的文章&#xff1a;在Ubunt…

玩转华为云Astro低代码体验季

目录 Astro轻应用应用场景 零代码应用构建 轻应用构建 行业应用构建 业务大屏构建 使用体验 功能建议 总体评价 Astro轻应用&#xff08;Astro Zero&#xff0c;简称AstroZero&#xff09;是华为云为行业客户、合作伙伴、开发者量身打造的低代码/零代码应用开发平台&#xff0c…

C++ A lambda function

lambda 函数是 C 中的匿名函数&#xff0c;可以内联定义并用作函数对象。 下面是定义 lambda 函数的一般语法&#xff1a; [capture list] (parameter list) -> return type { function body }lambda 语法的每个部分&#xff1a; - capture list&#xff1a;这是一个可选的…

R实践——【rgplates】功能函数解析

【rgplates】功能函数解析 1. 板块和特征重建2. 构造模型表示2.1 用法2.2 参数2.3 值2.4 示例 3. 实用工具3.1 用法3.2 参数3.3. 值3.4 示例 1. 板块和特征重建 一切与几何形状重建到过去状态有关的东西 reconstruct()&#xff1a;重建地理特征 详见R语言实践——古今地理坐…

4. WebGPU 存储缓冲区 (WebGPU Storage Buffers)

这篇文章是关于存储缓冲区的&#xff0c;我们从上一篇文章暂停的地方继续。 存储缓冲区在许多方面类似于统一缓冲区。如果我们所做的只是将 JavaScript 中的 UNIFORM 更改为 STORAGE 并将 WGSL 中的 var 更改为 var<storage, read> &#xff0c;那么上一页中的示例就可以…

Zabbix“专家坐诊”第193期问答汇总

问题一 Q&#xff1a;大佬们&#xff0c;怎么才能将zabbix-server接收到的数据全部展示出来呢&#xff1f;目前我的显示数据无法全部显示。 A&#xff1a;这个是用zabbix_sender发送过来的&#xff1f;确认下数据中是否包含空格等&#xff0c;如果有空格使用反斜杠转义或者单…

uniapp内置组件

目录 3.1、视图容器 view scroll-view swiper match-media 3.2、表单组件 form input App平台iOS端软键盘上方横条去除方案 关于软键盘弹出的逻辑说明 关于软键盘收起的逻辑说明 picker 3.3、 路由与页面跳转 navigator组件 3.4、 地图 map 3.1、视图容器 所有…

2.5. 重载与覆盖

在 Java 中&#xff0c;方法的重载&#xff08;Overloading&#xff09;和覆盖&#xff08;Overriding&#xff09;是两个重要的概念。它们都涉及到方法的定义与使用&#xff0c;但作用和规则有所不同。 重载&#xff08;Overloading&#xff09; 重载是指在同一个类中定义多…

2023年6月18日DAMA-CDGA/CDGP数据治理认证报名到这里

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

在家远程使用公司用友ERP财务软件 【远程办公】

文章目录 前言1.本地访问简介2. cpolar内网穿透3. 公网远程访问4. 固定公网地址 转发自cpolar极点云的文章&#xff1a;外网远程访问公司内网用友畅捷通T财务软件 – 远程办公 前言 用友畅捷通T适用于异地多组织、多机构对企业财务汇总的管理需求&#xff1b;全面支持企业对远…

webpack简单的搭建和使用(1)

随便创建一个空的文件夹&#xff0c;例如说&#xff1a;explore 然后我们测试一下我们的node是否存在 可以正确打印出版本 我们再次输入&#xff1a;npm init -y 创建一个package.json文件 出现这样的情况就成功了 然后我们要安装webpack在终端上输入命令&#xff1a; npm i …

数据结构图的基础概念

1、图的概念 图(Graph)&#xff1a;是由顶点的有穷非空集合和顶点之间边的集合组成。顶点(Vertex)&#xff1a;图中的数据元素。边(Edge)&#xff1a;顶点之间的逻辑关系,边可以是有向的或无向的&#xff0c;也可以带有权重&#xff08;可以表示距离&#xff0c;花费等&#xf…

GaussDB云数据库SQL应用系列-视图管理

一、前言 GaussDB是一款基于云计算技术的高性能关系型数据库&#xff0c;支持多种数据模型和分布式架构。在GaussDB中&#xff0c;视图管理是非常重要的一项功能&#xff0c;它可以帮助用户更方便地管理和查询数据。 数据库视图管理是指对数据库中的视图进行创建、修改、删除…

美债危机现曙光,比特币再破2万8

* * * 原创&#xff1a;刘教链 * * * 号外&#xff1a;今天在小号“刘教链Pro”发表了一篇《常常自律&#xff0c;偶尔放纵》&#xff0c;谈了一下关于意志力和自制力的非同寻常的科学研究结论&#xff0c;及其对投资方法的启迪&#xff0c;欢迎关注“刘教链Pro”并阅读。 * *…

AI专业教您保姆级在暗影精灵8Windows11上本地部署实现AI绘画:Stable Diffusion(万字教程,多图预警)

目录 一、Stable Diffusion介绍 二、Stable Diffusion环境搭建 1.Anaconda下载与安装 2.Pycharm&#xff08;IDE&#xff09;下载与安装 3.CUDA、CuDNN下载与安装 三、Stable Diffusion的本地部署 1.克隆项目到本地 2.初始化打开项目 3.安装环境所需库 4.运行代码…

《逆袭进大厂》之C++篇49问49答

它是在 github 上的 clone 下来的仓库笔记 自己看书理解到的知识点 网上相关问题的博客总结这几大基础上慢慢总结形成的&#xff0c;并不仅仅只是简单的收集整理&#xff0c;没有加入自己思考的笔记没有灵魂。 在接下来的十篇文章里我会陆陆续续将自己的秋招笔记整理出来&…

家政服务预约APP的系统设计与实现

摘 要&#xff1a;针对家政行业蓬勃发展&#xff0c;老套的家政服务方式已经跟不上互联网时代的步伐这个问题。基于Android移动平台的分析和设计过程、C/S模式、Eclipse平台&#xff0c;采用Java语言进行开发设计&#xff0c;设计了基于MVC架构的实现方案。安卓客户端与服务器…

solr教程

一&#xff1a;安装配置 下载完成之后&#xff0c;解压solr文件&#xff0c;解压tomcat 1.1 在tomcat安装solr,并且建立solrCore 把solr5.5目录下的server/solr-webapp/webapp 重命名为solr,并且放置到tomcat/webapp的目录下。 打开tomcat/webapp/solr/WEB-INF/web.xml新建…