spring boot的自动装配原理

一:简介

SpringBoot 这款框架几乎是现在企业级开发的标配,使用SpringBoot进行开发,能够大量减少xml配置文件的编写,并且能为我们提供一站式服务。SpringBoot我们只需要导入相关模块的starter,就可以使用相关功能,但是我们几乎没有写配置,那么SpringBoot是如何帮我们配置的呢?

又比如说我们可以在application.properties或者application.yml 配置文件中进行一些配置,比如设置 server.port=8081,那么我们是如何知道可以设置哪些属性呢?我们往往是通过记忆,或者通过IDEA提示,或者是官方文档,但是这些都不太行,只有我们真正研究透原理,才能随心所欲的去应用。
下面就来谈谈SpringBoot的自动装配原理。

二:原理

一切的开始,都要从启动类上的@SpringBootApplication注解开始。

@SpringBootApplication
public class AutoconfigurationApplication {
	public static void main(String[] args) {
		SpringApplication.run(AutoconfigurationApplication.class, args);
	}
}

这个注解又是一个合成注解,点进去看看。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

SpringBoot启动时,加载主配置类,并且开启了自动配置功能。也就是这个注解: @EnableAutoConfiguration

那么这注解的作用是什么呢?点进去看看

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

可以看到,主要利用到了一个底层的 @Import注解,导入一个选择器,根据选择器,给Spring的容器中导入一些组件。

那么导入哪些组件呢?点进这个 AutoConfigurationImportSelector 类看看。该类实现了 ImportSelect 接口,重写了这个方法:

String[] selectImports(AnnotationMetadata importingClassMetadata);

该方法返回的是一个String类型的数组,该数组其实就是一个全类名的数组,SpringBoot会将这些全类名对应的类加入到IOC容器中。重写如下:

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
// selectImports方法中调用了getAutoConfigurationEntry方法。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		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);
	}

可以看到,在 getAutoConfigurationEntry(),有这么一行代码

List configurations = getCandidateConfigurations(annotationMetadata, attributes);

通过getCandidateConfigurations方法,翻译过来是获取候选的配置,返回一个configurations对象。

那么这个getCandidateConfigurations方法的作用又是什么呢?点进去看看

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

调用了 SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); 这个方法传入了EnableAutoConfiguration.class以及类加载器。点进这个方法看看。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

分析一下上述代码,在loadFactoryNames方法中,其实起作用的是loadSpringFactories方法,在loadSpringFactories方法中,先通过类加载器去获取资源:classLoader.getResources(FACTORIES_RESOURCE_LOCATION);这个FACTORIES_RESOURCE_LOCATION是一个常量,表示 META-INF/spring.factories 这个路径。

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

所以说,当项目启动时,会去扫描所有jar包中的类路径下的META-INF/spring.factories 文件,获取文件对应的url。然后再遍历每一个url,最终将这些扫描到的文件的内容,包装成一个Properties对象。然后在从Properties中获取的值加入到返回的结果里面。这个结果就是要交给容器的所有组件的全类名。

这里的factoryType就是传过来的EnableAutoConfiguration.class。所以会从properties中获取这个类名对应的值,然后把他们添加到容器中。

一共131个。

这131给 xxxAutoConfiguration 都是容器中的一个组件,都会加入到容器中,这些自动配置类本质都是配置类,用他们来进行自动配置。

就拿HttpEncodingAutoConfiguration为例,来解释自动配置原理。

@Configuration(proxyBeanMethods = false) // 表示是一个配置类,可以给容器中添加组件
@EnableConfigurationProperties(ServerProperties.class) // 启动指定类的 @ConfigurationProperties 功能,让指定的类和 properties 文件进行属性映射,并将ServerProperties加入到容器中,给其他组件使用。
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)// @Condition,表示如果是web应用,才生效
@ConditionalOnClass(CharacterEncodingFilter.class) // 判断当前项目有没有这个类,这个类是MVC中进行乱码解决的过滤器
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) // 判断配置文件中是否存在 server.servlet.encoding 这个配置,即使不存在也判断成立。即使不配置,也默认生效。
public class HttpEncodingAutoConfiguration {
    // 这个properties已经和配置文件映射了。
    private final Encoding properties;
    // 只有一个有参构造器,这个参数的值就会从容器中拿,
    public HttpEncodingAutoConfiguration(ServerProperties properties) {
		this.properties = properties.getServlet().getEncoding();
	}
}

点进 ServerProperties 这个类看看,可以看到,标注了@ConfigurationProperties注解,该注解的作用就是从配置文件中获取指定的值和bean的属性进行绑定。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

	/**
	 * Server HTTP port.
	 */
	private Integer port;

所以说,我们想要知道配置文件中去配置什么,就可以看 @ConfigurationProperties 注解的prefix 前缀。 可以看到,我们常用的server.port就是这么来的。

也就是说,所有的在配置文件中配置的属性都是在xxxProperties中封装着。 配置文件能配什么,就可以参照某个功能对应的属性类。

HttpEncodingAutoConfiguration类上标注了很多的 @Condition 注解,如果这些条件都成立,配置类才会生效。如果生效,就会向容器中@Bean,添加组件,这些值的的某些属性,需要从properties中获取。

@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	}

我们在application.properteis 中直接点击我们配置的属性,也会跳转到绑定的properties文件中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习基于Python+TensorFlow+Django的水果识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介技术组合系统功能使用流程 二、功能三、系统四. 总结 一项目简介 # 深度学习基于PythonTensorFlowDjango的水果识别系统介绍 简介 该水果识别系统基于…

医保线上购药系统:引领医疗新潮流

在科技的驱动下&#xff0c;医疗健康服务正经历一场数字化的革新。医保线上购药系统&#xff0c;不仅是一种医疗服务的新选择&#xff0c;更是技术代码为我们的健康管理带来的全新可能。本文将通过一些简单的技术代码示例&#xff0c;深入解析医保线上购药系统的工作原理和优势…

C#,《小白学程序》第五课:队列(Queue)其一,排队的技术与算法

日常生活中常见的排队&#xff0c;软件怎么体现呢&#xff1f; 排队的基本原则是&#xff1a;先到先得&#xff0c;先到先吃&#xff0c;先进先出 1 文本格式 /// <summary> /// 《小白学程序》第五课&#xff1a;队列&#xff08;Queue&#xff09; /// 日常生活中常见…

uniapp IOS从打包到上架流程(详细简单) 原创

uniapp打包好的ipa文件&#xff0c;需要上架到appstore&#xff0c;用户才能通过app store下载使用&#xff0c;因此我们需要将ipa上架到appstore. 我们这篇文章&#xff0c;将教会大家使用windows电脑将uniapp打包好的ipa文件&#xff0c;上架到appstore的方法和详细流程。 …

SpectralGPT: Spectral Foundation Model 论文翻译2

遥感领域的通用大模型 2023.11.13在CVPR发表 原文地址&#xff1a;[2311.07113] SpectralGPT: Spectral Foundation Model (arxiv.org) 实验 ​ 在本节中&#xff0c;我们将严格评估我们的SpectralGPT模型的性能&#xff0c;并对其进行基准测试SOTA基础模型&#xff1a;ResN…

C#/.NET/.NET Core推荐学习书籍(已分类)

前言 古人云&#xff1a;“书中自有黄金屋&#xff0c;书中自有颜如玉”&#xff0c;说明了书籍的重要性。作为程序员&#xff0c;我们需要不断学习以提升自己的核心竞争力。以下是一些优秀的C#/.NET/.NET Core相关学习书籍&#xff0c;值得.NET开发者们学习和专研。书籍已分类…

java学习part10 this

90-面向对象(进阶)-关键字this调用属性、方法、构造器_哔哩哔哩_bilibili 1.java的this java的this性质类似cpp的this&#xff0c; 但它是一种引用&#xff0c;所以用 this. xxx来调用。 this代表当前的类的实例&#xff0c;所以必须和某个对象结合起来使用&#xff0c;不能…

elk 简单操作手册

1.1. 基础概念 EFK不是一个软件,而是一套解决方案,开源软件之间的互相配合使用,高效的满足了很多场合的应用,是目前主流的一种日志系统。 EFK是三个开源软件的缩写,分别表示:Elasticsearch , Filebeat, Kibana , 其中Elasticsearch负责日志保存和搜索,Filebeat负责收集日志,Ki…

【开源】基于Vue和SpringBoot的独居老人物资配送系统

项目编号&#xff1a; S 045 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S045&#xff0c;文末获取源码。} 项目编号&#xff1a;S045&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询社区4…

【maven】【IDEA】idea中使用maven编译项目,报错java: 错误: 找不到符号 【2】

idea中使用maven编译项目,报错java: 错误: 找不到符号 错误状况展示: 如果报这种错,是因为项目中真的找不到报错的方法或者枚举 字段之类的,但实际是 : 点击 File Path

上市公司-股权性质数据(国企、央企)2003-2022年

上市公司-股权性质数据&#xff08;国企、央企&#xff09;是一个针对上市公司的数据集&#xff0c;主要涵盖了A股公司股权性质的详细信息&#xff0c;区分了公司是否为民营企业、国企或央企。这份数据集提供了每家上市公司的股权结构背景&#xff0c;对投资者、市场分析师和经…

绿色能源守护者:光伏运维无人机

随着我国太阳能光伏产业被纳入战略性新兴产业&#xff0c;光伏发电成为实现“双碳”目标的关键之一。在政策支持下&#xff0c;光伏产业维持高速发展&#xff0c;为迎接“碳达峰、碳中和”大势注入了强大动力。在这一背景下&#xff0c;复亚智能与安徽一家光伏企业合作&#xf…

(Matalb时序预测)GA-BP遗传算法优化BP神经网络的多维时序回归预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码 四、本文代码数据说明手册分享&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matalb平台编译&am…

【开源】基于Vue和SpringBoot的企业项目合同信息系统

项目编号&#xff1a; S 046 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S046&#xff0c;文末获取源码。} 项目编号&#xff1a;S046&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合…

第98步 深度学习图像目标检测:SSD建模

基于WIN10的64位系统演示 一、写在前面 本期开始&#xff0c;我们继续学习深度学习图像目标检测系列&#xff0c;SSD&#xff08;Single Shot MultiBox Detector&#xff09;模型。 二、SSD简介 SSD&#xff08;Single Shot MultiBox Detector&#xff09;是一种流行的目标检…

【Qt之QTextDocument】使用及表格显示富文本解决方案

【Qt之QTextDocument】使用 描述常用方法及示例使用QTextList使用QTextBlock使用QTextTable表格显示富文本结论 描述 QTextDocument类保存格式化的文本。 QTextDocument是结构化富文本文档的容器&#xff0c;支持样式文本和各种文档元素&#xff0c;如列表、表格、框架和图像。…

类和对象(下)

目录 1.初始化列表 1.1 构造函数体内的赋值 1.2 初始化列表 1.对象整体定义和成员变量定义的区别 2.初始化列表的写法 1.3 和C11的联系 1.4 针对初始化列表的建议 2.静态成员 2.1 静态成员变量 1.概念 2.特性 2.2 静态成员函数 1.概念 2.特性 3.友元 3.1 友元函…

win10安装pytorch(py39)

cuda≤11.6&#xff0c;观察控制面板 观察torch对应cuda版本 https://download.pytorch.org/whl/torch/ 安装cuda11.6.0 CUDA Toolkit Archive | NVIDIA Developer cmd输入nvcc -V 编辑国内镜像源 .condarc anaconda prompt输入 查看环境 conda env list 安装py3.9…

【初始前后端交互+原生Ajax+Fetch+axios+同源策略+解决跨域】

初始前后端交互原生AjaxFetchaxios同源策略解决跨域 1 初识前后端交互2 原生Ajax2.1 Ajax基础2.2 Ajax案例2.3 ajax请求方式 3 Fetch3.1 fetch基础3.2 fetch案例 4 axios4.1 axios基础4.2 axios使用4.2.1 axios拦截器4.2.2 axios中断器 5 同源策略6 解决跨域6.1 jsonp6.2 其他技…

线程-Thread类及常见方法

目录 一、创建线程 1.继承 Thread 类 2. 实现 Runnable 接口 3.匿名内部类创建 Thread 子类对象 4. 匿名内部类创建 Runnable 子类对象 5. lambda 表达式创建 Runnable 子类对象 二、Thread 类及常见方法 2.1 Thread 的常见构造方法 2.2 Thread 的几个常见属性 2.3 启…