SpringBoot 自动装配原理

news2024/12/25 12:15:50

什么是自动装配

  • springboot 定义一套接口规范,这套规范规定:springboot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 spring 容器,并执行类中定义的各种操作
  • 对于外部 jar 来说,只需要按照 springboot 定义的标准,就能将自己的功能装置进 springboot

自动装配的作用

  • 自动装配能够很大程度减少开发人员对 bean 的装配工作,只需要通过把对应的 bean 对象配置到对应工程下 META-INF/spring.factories,就能够装配到 spring 容器中

DeferredImportSelector 接口分析

  • springboot 的 AutoConfigurationImportSelector 类实现 DeferredImportSelector 接口,并且实现内部接口 Group,重写 Group 的 process() 和 selectImports() 方法
  • DeferredImportSelector 接口的 process() 和 selectImports() 方法,在 spring 调用bean定义注册后置处理器的时候会调用这个两个方法
  • process() 方法会获取 spring.factories 中用户定义的 bean 列表,selectImports() 方法获取将 process() 方法获取的 bean 对象列表包装成 Group 接口的内部类 Entry 对象
  • 在获取到 Entry 对象列表之后,将这些 bean 对象注册到容器中
  • 之后 spring 会对容器中的 bean 对象列表进行实例化和属性填充
/**
 * 继承 ImportSelector 接口
 */
public interface DeferredImportSelector extends ImportSelector {

	/**
	 * 返回 Group 接口实现类的 class
	 */
	@Nullable
	default Class<? extends Group> getImportGroup() {
		return null;
	}


	/**
	 * Group 接口,使用必须实现这个类
	 */
	interface Group {

		/**
		 * 上面分组完成后 spring 会调用该方法,循环 List 里的 DeferredImportSelector 类,并循环调用 process()
		 */
		void process(AnnotationMetadata metadata, DeferredImportSelector selector);

		/**
		 * 每个 Group 只执行一次,返回一个迭代器,spring 会使用迭代器的 forEach 方法进行迭代,想要导入 spring 容器的类要封装成 Entry 对象
		 */
		Iterable<Entry> selectImports();


		/**
		 * Entry 实体类,持有注解和需要导入的类名称
		 */
		class Entry {

			private final AnnotationMetadata metadata;

			private final String importClassName;

			public Entry(AnnotationMetadata metadata, String importClassName) {
				this.metadata = metadata;
				this.importClassName = importClassName;
			}

			public AnnotationMetadata getMetadata() {
				return this.metadata;
			}

			public String getImportClassName() {
				return this.importClassName;
			}

			@Override
			public boolean equals(@Nullable Object other) {
				if (this == other) {
					return true;
				}
				if (other == null || getClass() != other.getClass()) {
					return false;
				}
				Entry entry = (Entry) other;
				return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
			}

			@Override
			public int hashCode() {
				return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
			}

			@Override
			public String toString() {
				return this.importClassName;
			}
		}
	}

}


自动装配实现原理

AutoConfigurationGroup 实现类分析

  • spring 在容器刷新的时候会调用这两个方法
  • 调用两个方法之后,用户定义的 bean 对象会被注入到 spring 容器中,之后会统一对bean对象进行实例化和属性填充
// 解析用户定义在 spring.factories 中的 bean 对象
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
	// 确定是 AutoConfigurationImportSelector 子类
	Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
			() -> String.format("Only %s implementations are supported, got %s",
					AutoConfigurationImportSelector.class.getSimpleName(),
					deferredImportSelector.getClass().getName()));
	// 从 "META-INF/spring.factories" 文件获取需要的自动配置类
	AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
			.getAutoConfigurationEntry(annotationMetadata);
	// 将需要的类传入全局变量,以便 selectImports 方法使用
	this.autoConfigurationEntries.add(autoConfigurationEntry);
	for (String importClassName : autoConfigurationEntry.getConfigurations()) {
		this.entries.putIfAbsent(importClassName, annotationMetadata);
	}
}

// 将获取出来的bean对象排序、过滤包装成 DeferredImportSelector.Group.Entry 对象
@Override
public Iterable<Entry> selectImports() {

	// 没有需要自动装配的类,返回空集合
	if (this.autoConfigurationEntries.isEmpty()) {
		return Collections.emptyList();
	}

	// 去除不需要的
	Set<String> allExclusions = this.autoConfigurationEntries.stream()
			.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
	Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
			.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
			.collect(Collectors.toCollection(LinkedHashSet::new));
	processedConfigurations.removeAll(allExclusions);

	// 排序,封装对象并返回集合
	return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
			.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
			.collect(Collectors.toList());
}

  • 获取 spring.factories 中的 bean 对象列表
// 获取 spring.factories 中的 bean 对象
protected AutoConfigurationEntry  getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 获取注解的属性的内容
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 获取候选 bean,获取 spring.factories 中定义的bean
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// 去重
		configurations = removeDuplicates(configurations);						
		// 去除排除的类					
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);			
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

流程总结

  • springboot 自动装配的流程
  • spring 容器刷新对 DeferredImportSelector 的处理

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

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

相关文章

Java并发编程—java内存模型1

文章目录Java内存模型的基础并发编程模型的两个关键性问题1、线程之间如何通信&#xff1f;(问题1)2、进程之间如何通信&#xff1f;(问题2)线程间通信机制&#xff1a;共享内存、消息传递1、共享内存2、消息传递java内存模型抽象结构指令重排序并发编程模型的分类happens-befo…

资料库的webrtc文件传输

一、一个看似简单的事情往往不简单 一个简单的事情往往会倾注你的心血&#xff0c;也许你看到很简单往往其实没那么简单&#xff1b;其实想想今年业余时间的大把代码&#xff0c;真正能成品的好像并不多。 马上年底了&#xff0c;写下这篇文章。每一行程序就像写小说一样&…

Egg 1. 快速开始 Quick Start 1.3 一步步 Step by Step 1.3.1 初始化 1.3.2 创建控制器

Egg Egg 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Egg1. 快速开始 Quick Start1.3 一步步 Step by Step1.3.1 初始化1.3.2 创建控制器1. 快速开始 Quick Start 1.3 一步步 Step by Step 之前我们直接使用 npm init egg&#xff0c;选择了…

# 智慧社区管理系统-基础管理-04业主管理

一后端 1entity package com.woniu.community.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data AllArgsConstructor NoArgsConstructor public class Owner {private int id;private String userName;private String …

Linux I2C驱动框架, 调试神奇I2C-Tools介绍

1. IIC协议 2. Linux的I2C体系结构分为3个组成部分&#xff1a; I2C核心( i2c-core.c )&#xff1a;   I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法、I2C通信方法(”algorithm”)、与具体适配器无关的代码、探测设备、检测设备地址的上层代码等。 …

在线PDF查看器和PDF编辑器:GrapeCity Documents PDF (GcPdf)

跨平台 JavaScript PDF 查看器 使用我们的 JavaScript PDF 查看器在网络上阅读和编辑 PDF。跨浏览器和框架打开和打印。GrapeCity Documents PDF (GcPdf) 全功能的 JavaScript PDF 查看器和 PDF 编辑器 适用于所有现代浏览器和框架&#xff0c;包括 Edge、Chrome、Firefox、Op…

UML类图中 前缀符号 + - # ~ 的含义

UML类图中有各种符号&#xff0c;新手看着一脸懵逼&#xff0c;今天我就来讲一下各个符号到底是啥意思。 其实这些符号都是在描述类属性的可见性&#xff1a; UML中&#xff0c;可见性分为4级 1、public public 公用的用 前缀表示 &#xff0c;该属性对所有类可见 2、prote…

算法篇-----回溯1

文章目录什么是回溯呢&#xff1f;力扣690-----员工的重要性&#xff08;中等&#xff09;力扣733-----图像渲染&#xff08;简单&#xff09;力扣463-----岛屿的周长&#xff08;简单)力扣130------被围绕的区域&#xff08;中等&#xff09;力扣17--------电话号码的组合 &am…

kafka-consumer-offset位移

目录 1 offset的默认维护位置 1.1 消费offset案例 2 自动提交offset 3 手动提交offset 3.1 原理 3.2 代码示例 3.2.1 同步提交 3.2.2 异步提交(生产常用) 4 指定offset消费 5 指定时间消费 6 漏消费和重复消费分析 6.1 重复消费 6.2 漏消费 6.3 消费者事务 7 数据…

高通开发系列 - ALSA声卡驱动中tinymix返回时间过慢

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 问题背景问题分析验证第一个猜测验证第二个猜测问题原因解决方案问题背景 我们一个高通平台上出现一个问题: tingmix命令需要几秒钟…

一文带你深入理解Linux端口重用这一特性

【好文推荐】 需要多久才能看完linux内核源码&#xff1f; 概述Linux内核驱动之GPIO子系统API接口 一篇长文叙述Linux内核虚拟地址空间的基本概括 轻松学会Linux下查看内存频率,内核函数,cpu频率 大家好&#xff0c;我是Linux吴彦祖&#xff01; 开篇我先考大家一个小问题&…

Golang Map 基本原理

Go 语言中的 map 即哈希表。哈希表把元素分到多个桶里&#xff0c;每个桶里最多放8个元素。在访问元素时&#xff0c;首先用哈希算法根据 key 和哈希表种子获得哈希值(暂将其命名为 h)&#xff0c;然后利用 h 的低 bbb 位得到桶的序号。其中桶的个数为 2b2^b2b 个&#xff0c;是…

乐趣国学—品读“富润屋,德润身。”中的智慧

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨当前专栏&#xff1a;国学周更-心性养成之路 …

java 基于 SpringMVC+Mybaties+ easyUI 快递公司管理系统 的 设计与实现

一.项目介绍 本系统 角色 权限 动态配置 默认配置了三种 第一种&#xff1a; 超级管理员 第二种&#xff1a; 运输公司 第三种&#xff1a; 订单跟踪人员 超级管理员拥有所有权限&#xff0c;包括车子、路线、订单、是否送达以及交易的统计报表 运输公司&#xff1a;车辆管理权…

使用 Python 和 Streamlit 创建一个很棒的 Web 应用程序

“我们如何制作一个机器学习脚本并将其转换为一个尽可能简单的应用程序,让它基本上感觉像是一个脚本练习?” — Adrien Treuille(Streamlit 的发明者) Web 应用程序是显示数据科学或机器学习项目结果的好方法。从头开始开发 Web 应用程序需要大量时间、精力和技术技能。另一…

世界杯海信再出圈,三星:“谈不上愉悦”

作者 | 曾响铃 文 | 响铃说 本届世界杯作为第一次在北半球冬季举行的世界杯&#xff0c;给全世界球迷带去了一次全新体验。且随着赛程的推进&#xff0c;更多的“惊喜”也一一浮现。 其一便是超多的爆冷&#xff0c;虽然没有具体统计&#xff0c;但此次应该是近几届爆冷最多…

[激光原理与应用-32]:典型激光器 -4- 半导体泵浦固体激光器

目录 第1章 概述 1.1 什么是半导体泵浦固体激光器 1.2 优势 1.3 典型的波长 第2章 半导体泵浦固体激光器的种类 2.1 端面泵浦固体激光器 2.2 侧面泵浦固体激光器 第1章 概述 1.1 什么是半导体泵浦固体激光器 半导体泵浦固体激光器&#xff08;Diode Pump Solid State …

Python函数

一、函数介绍 函数&#xff1a;是组织好的&#xff0c;可重复使用的&#xff0c;用来实现特定功能的代码段。 使用函数的好处是&#xff1a; 将功能封装在函数内&#xff0c;可供随时随地重复利用提高代码的复用性&#xff0c;减少重复代码&#xff0c;提高开发效率二、函数…

学习python第一天

关于Python的数据类型 Python数据类型包括&#xff1a; 数字类型&#xff0c;字符类型&#xff0c;布尔类型&#xff0c;空类型&#xff0c;列表类型&#xff0c;元组类型&#xff0c;字典类型 1、数字类型 包括&#xff1a;整型int 浮点型float(有小数位的都是是浮点型) 注…

代码随想录刷题|LeetCode 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划

目录 1143.最长公共子序列 思路 1、确定dp数组 2、确定递推公式 3、dp数组初始化 4、遍历顺序 5、推导dp数组 最长公共子序列 1035.不相交的线 思路 不相交的线 53. 最大子序和 思路 最大子序列 动态规划 贪心算法 1143.最长公共子序列 题目链接&#xff1a;力扣 思路 不知道…