第六节 自动装配源码理解

tips:不同版本代码实现有差异。

前面两章了解的流程,就是 SpringBoot 自动转配的核心。

一、自动装配

1.1 什么是 SpringBoot 自动装配?

自动装配是 Spring 框架用来减少配置的显式需求而引入的一个特性,该特性通过 @Autowired或者@Resource注解实现依赖对象的自动注入。而 Spring Boot 在此基础上进一步发展,提出了更高级的自动配置(Auto-configuration)概念。SpringBoot 在启动时会扫描外部 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装配到 Spring 容器里面。

自动装配机制是Spring Boot提供的核心特性之一,它极大地提高了开发效率,同时确保了配置的简洁性和灵活性,实现“约定大于配置”

1.2 自动装配的工作原理

自动装配,抓住三个要素。(在前面章节我们已经分析了其核心流程)

  1. @EnableAutoConfiguration
  2. AutoConfigurationImportSelector
  3. spring.factories

AutoConfigurationImportSelector是自动装配背后的关键角色。实现自ImportSelector接口,它会从 META-INF/spring.factories 文件中加载EnableAutoConfiguration指定的配置类。这是通过SpringFactoriesLoader类来实现的,它为Spring Boot自动装配提供了加载和解析spring.factories文件的能力。

spring.factories: 位于META-INF目录下的spring.factories文件包含了自动配置类的全限定名列表。当应用启动时,这些配置类会被加载并根据条件判断是否应用到当前的应用上下文中。

@EnableAutoConfiguration 作为自动装配的关键。

二、从注解入手

以 mybatis-starter-apply 为例子开始

gitee地址:uzong-starter-learning: 学习 SpringBoot Starter 的工程案例

在我们启动类上有这样的注解 SpringBootApplication,它是开启配置自动装配的大门钥匙。

@SpringBootApplication

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

分析 @SpringBootApplication 注解, 它是多个注解的集成。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

  • @Configuration: SpringBootConfiguration 等于 Configuration。属于派生。
  • @EnableAutoConfiguration: 负责激活 SpringBoot 自动装配机制
  • @ComponentScan: 路径扫描,该扫描路径下的 bean 都会被扫描加载,另外可以排除不需要的类。 即激活 @Conponent 的扫描

@SpringBootApplication 等价于上面三个注解。而实现自动装配的关键注解是 @EnableAutoConfiguration

@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
  • @Import 导入 AutoConfigurationImportSelector 配置类,它会加载各种自动化配置类, 它是实现 Starter 的核心
  • @AutoConfigurationPackag: 由AutoConfigurationPackages.Registrar(一个实现了ImportBeanDefinitionRegistrar的类)处理。默认值是启动类所在的包路径,默认指定启动类路径下的类加载到 Spring 容器。主要逻辑在 AutoConfigurationPackages#register 方法中。 该方法有两个参数 registry 和 packageNames。packageNames 的值默认是启动类包所在的路径。如下所示:

对应的栈帧

registry 即 DefaultListableBeanFactory

Lite && Full (拓展理解)

@Bean 的声明方式为“轻量模式 Lite”

@Configuration 下声明的@Bean为“完全模式Full”,存在 cglib 提升

三、AutoConfigurationImportSelector

关于 AutoConfigurationImportSelector, 它实现了 ImportSelector 接口(3.x 开始)

3.1 ImportSelector

public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.*/String[] selectImports(AnnotationMetadata importingClassMetadata);}

ImportSelector 用于条件性的导入配置类。是Spring框架处理@Import注解的一部分,提供了一种灵活的方式来根据运行时的条件动态决定 Spring 配置; 尤其是在开发自定义自动配置和框架扩展时。

selectImports方法的返回值是一个包含配置类全路径名称(Fully qualified names)的字符串数组

ImportSelector可以与注解一起使用,让开发者在@Configuration注解的类上通过@Import来导入它。它不直接将配置类加入到Spring上下文中,而是提供一种选择性地导入配置类的方式。

通过ImportSelector,开发者可以在运行时根据条件(比如classpath中是否存在某个类,或者某个属性是否被定义等)来决定是否导入某些配置。这提供了极大的灵活性,使得 Spring Boot 的自动配置成为可能。

开发一个可插拔的模块或框架时,ImportSelector可以用来根据应用的配置或依赖来动态导入配置类。Spring Boot 大量使用了ImportSelector来实现自动配置。通过条件检查,只有当满足特定条件时,如果类路径中有特定的类存在,或某些属性被定义,相关的配置类才会被导入

3.2 DeferredImportSelector

/*** A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans* have been processed. This type of selector can be particularly useful when the selected* imports are {@code @Conditional}.** <p>Implementations can also extend the {@link org.springframework.core.Ordered}* interface or use the {@link org.springframework.core.annotation.Order} annotation to* indicate a precedence against other {@link DeferredImportSelector}s.** @author Phillip Webb* @since 4.0*/
public interface DeferredImportSelector extends ImportSelector {}

A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans

have been processed

DeferredImportSelectorImportSelector的一个扩展接口,它在常规的ImportSelector执行之后稍晚运行。这允许其他的配置类先被处理,使得DeferredImportSelector可以在做决定时考虑到这些先前的配置。

而 AutoConfigurationImportSelector 就是实现了 DeferredImportSelector 该接口。

ImportSelector是Spring框架中一个强大的特性,它为条件化配置和自动配置提供了强大的支撑,是实现灵活、动态导入Spring配置的关键机制。通过ImportSelector和它的扩展,Spring实现了一套强大的配置和自动配置机制,极大地简化了Spring应用的配置工作。

3.3 AutoConfigurationImportSelector

核心方法 selectImports

自动配置机制能够有条件地导入合适的配置,这对整合第三方库、开发自定义 starter 或在运行时动态调整配置都是至关重要的

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {// 检查自动配置是否启用,如果没有就直接返回一个空的导入数组。if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}try {// 加载自动配置元数据,可能包括配置类的属性、条件以及其他相关信息。AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);// 取得 @EnableAutoConfiguration 注解的属性,这可能含有排除某些配置类的信息。AnnotationAttributes attributes = getAttributes(annotationMetadata);// 获取所有候选的自动配置类列表。List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);// 移除配置类列表中的重复元素。configurations = removeDuplicates(configurations);// 基于AutoConfigurationMetadata将配置类列表排序。configurations = sort(configurations, autoConfigurationMetadata);// 获取所有排除的配置类。Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 校验排除的类是否真正存在于候选配置中,并抛出异常如果有必要。checkExcludedClasses(configurations, exclusions);// 从候选列表中移除所有排除的类。configurations.removeAll(exclusions);// 过滤掉那些不应被导入的配置类。configurations = filter(configurations, autoConfigurationMetadata);// 触发一系列自动配置导入事件。fireAutoConfigurationImportEvents(configurations, exclusions);// 将配置类列表转换为字符串数组返回。return StringUtils.toStringArray(configurations);}......
}

3.3 isEnabled

protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,true);
}
return true;
}

首先调用 isEnabled 方法去判断自动化配置到底有没有开启,可以通过在 application.properties 中配置 spring.boot.enableautoconfiguration=false 来关闭所有的自动化配置。

3.4 getCandidateConfigurations

获取 claspath\:META-INF/spring.factories 中所有的自动装配类。

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;
}

注意:getSpringFactoriesLoaderFactoryClass(), 返回的是 EnableAutoConfiguration.class。

到这里,我们应该知道 spring.factories 的前缀,为什么要定义成org.springframework.boot.autoconfigure.EnableAutoConfiguration 这种固定key了吧。

3.5 removeDuplicates

除候选自动化配置类中重复的类。借助 LinkedHashSet 去除重复

protected final <T> List<T> removeDuplicates(List<T> list) {return new ArrayList<>(new LinkedHashSet<>(list));
}

3.6 getExclusions

	protected Set<String> getExclusions(AnnotationMetadata metadata,AnnotationAttributes attributes) {Set<String> excluded = new LinkedHashSet<>();excluded.addAll(asList(attributes, "exclude"));excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded;}

getExclusions 获取到所有被排除的自动化配置类。

  1. 当前注解的 exclude 属性获取
  2. 当前注解的 excludeName 属性获取
  3. getExcludeAutoConfigurationsProperty() 从配置文件中的 spring.autoconfigure.exclude 属性获取

3.7 checkExcludedClasses

校验不是 not auto-configuration 类则不能使用 exclude 方式做类的排除。

3.8 filter

加载完所有的自动化配置类了,但是,这些配置类是否生效,还需要根据当前项目的依赖等进行加载生效。

比如:MybatisAutoConfiguration 是否能够加载,需要查看当前的依赖是否存在,比如 SqlSessionFactory.class

for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;skipped = true;}}
}

处理下面两个注解

  1. ConditionalOnClass
  2. ConditionalOnMissingClass

3.9 其他方法

fireAutoConfigurationImportEvents:于那些想要在导入配置之前进行特定操作的扩展点。

3.10 小结

到这里,关于 SpringBoot 的Starter 能够进行扩展,来源于 ImportSelector 实现类。 它是能够实现扩展的核心。AutoConfigurationImportSelector 是 SpringBoot 中的类, 但是 ImportSelector 是springframework core 中的类。

这些 Configuration 类,将使用 ConfigurationClassParse 进行解析。

四、 ConfigurationClassParse

processDeferredImportSelectors 导入配置类。

private void processDeferredImportSelectors() {for (DeferredImportSelectorHolder deferredImport : deferredImports) {ConfigurationClass configClass = deferredImport.getConfigurationClass();try {String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);}......}
}

五、本章小结

本章能够实现扩展,是来源于 ImportSelector(DeferredImportSelector) 接口。AutoConfigurationImportSelector(SpringBoot) 整好扩展这个类,使得 Starter 模式能够非常优雅的进行扩展。

那么下一章,我们将进一步扩展理解 ConfigurationClassParse 这部分源码。

已同步发布到公众号:面汤放盐 第六节 自动装配源码理解 (qq.com)

掘金账号:第六节 自动装配源码理解 - 掘金 (juejin.cn)

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

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

相关文章

Redis数据库知识点

Redis set get del keys redis中有哪些数据类型 string 最大512m key层级 redis的key允许有多个单词形成层级结构&#xff0c;多个单词之间用‘:’隔开 set get del keys hash 本身在redis中存储方式就为key-value, 而hash数据结构中value又是一对key-value hset key …

【easyx】快速入门——弹球小游戏(第一代)

目录 1.需求 2.运动的小球 3.碰到边缘反弹 4.圆周撞击或越过边界反弹 5.绘制和移动挡板 6.小球碰到挡板反弹 7.游戏失败时该如何处理 8.随机初始条件 9.完整代码 我们这一节将结合动画和键盘交互的知识来做一个小游戏 1.需求 我们先看需求:小球在窗体内运动,撞到除…

后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操

之前的SQL语句是基于注解 以后开发中一般是一个接口对应一个映射文件 书写映射文件 基本结构 框架 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.or…

尽在掌握:Android 13 通知新功能详解

尽在掌握&#xff1a;Android 13 通知新功能详解 在移动应用开发中&#xff0c;通知扮演着至关重要的角色&#xff0c;它如同应用程序与用户之间的桥梁&#xff0c;及时传递关键信息&#xff0c;提升用户体验。Android 13 作为最新的安卓版本&#xff0c;在通知方面带来了诸多…

QLExpress入门及实战总结

文章目录 1.背景2.简介3.QLExpress实战3.1 基础例子3.2 低代码实战3.2.1 需求描述3.2.1 使用规则引擎3.3.2 运行结果 参考文档 1.背景 最近研究低代码实现后端业务逻辑相关功能&#xff0c;使用LiteFlow作为流程编排后端service服务, 但是LiteFlow官方未提供图形界面编排流程。…

使用RAG和文本转语音功能,我构建了一个 QA 问答机器人

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

代码随想录算法训练营第36期DAY37

DAY37 先二刷昨天的3道题目&#xff0c;每种方法都写&#xff1a;是否已完成&#xff1a;是。 报告&#xff1a;134加油站的朴素法没写对。原因是&#xff1a;在if中缺少了store>0的判断&#xff0c;只给出了indexi的判断。前进法没写出来。因为忘记了总油量的判断。Sum。…

VMware 安装Windows Server 2008 R2

1.下载镜像 迅雷&#xff1a;ed2k://|file|cn_windows_server_2008_r2_standard_enterprise_datacenter_and_web_with_sp1_x64_dvd_617598.iso|3368839168|D282F613A80C2F45FF23B79212A3CF67|/ 2.安装过程 自定义名字&#xff0c;点击【浏览】选择安装路径 点击【浏览】选择前…

(Oracle)SQL优化基础(三):看懂执行计划顺序

往期内容&#xff1a; &#xff08;Oracle&#xff09;SQL优化基础&#xff08;一&#xff09;&#xff1a;获取执行计划 &#xff08;Oracle&#xff09;SQL优化基础&#xff08;二&#xff09;&#xff1a;统计信息 获取到执行计划后&#xff0c;对于新手朋友来讲可能不知道…

Qt笔记:动态处理多个按钮点击事件以更新UI

问题描述 在开发Qt应用程序时&#xff0c;经常需要处理多个按钮的点击事件&#xff0c;并根据点击的按钮来更新用户界面&#xff08;UI&#xff09;&#xff0c;如下图。例如&#xff0c;你可能有一个包含多个按钮的界面&#xff0c;每个按钮都与一个文本框和一个复选框相关联…

基于springboot+vue+Mysql的逍遥大药房管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

【flutter】 Running Gradle task ‘assembleDebug‘...超时问题

关联搜索&#xff1a;flutter下载gradle失败、AndroidStudio下载gradle失败 构建Flutter项目时遇到控制台一直卡在 Running Gradle task ‘assembleDebug’… 解决方案 1. 修改gradle-wrapper.properties 文件 如果找不到就直接搜索&#xff1a; 把https\://services.gradl…

vscode更改语言,记录一下

首先打开安装好的Vscode软件&#xff0c;可以看到页面上显示的是英文效果。 同时按键ctrlshiftp&#xff0c;接着在输入框中输入 configure Display language如图&#xff1a; 选择中文简体就ok了&#xff0c;如果没有则安装 chinese Language pack

vue3 ElementUI 日期禁选当日前, 当日后,几天后,几天前(例如3天后)

今日之前禁用 代码: ( 主要是 :disabledDate“disabledDateFun” ) <el-date-picker v-model"queryForm.selectedDate"type"date"range-separator"-"placeholder"选择日期":disabledDate"disabledDateFun" clearable /&…

前端面试:项目细节重难点问题分享

面试官提问&#xff1a;我现在给你出一个项目实际遇到的问题&#xff1a;由于后端比较忙&#xff0c;所以我们这边的列表数据排序需要前端最近实现&#xff0c;那你会怎么实现排序呢&#xff1f; 答&#xff1a;我的回答&#xff1a;确实&#xff0c;数据都是由后端实现的&…

基于MetaGPT构建单智能体

前言 在之前的文章中&#xff0c;我们详细地描述了Agent的概念和组成&#xff0c;在代码案例中体验了Agent的记忆、工具、规划决策模块&#xff0c;并通过几个Agent框架来加强读者对Agent开发设计与应用的理解&#xff0c;接下来我们就要进入智能体Agent的实际开发中&#xff0…

教师专属的成绩发布小程序

还在为成绩发布而烦恼&#xff1f;还在担心家长无法及时获得孩子的学习反馈&#xff1f;是否想要一个既安全又高效的工具来简化你的教学工作&#xff1f;那么&#xff0c;易查分小程序可能是你一直在寻找的答案。 现在的老师们有了超多的工具来帮助我们减轻负担&#xff0c;提高…

多式联运奇迹:探索 GPT-4o 的尖端功能

取得的显着进展的DigiOps与人工智能已经标志着重要的里程碑&#xff0c;随着时间的推移塑造了人工智能系统的能力。从早期基于规则系统的出现机器学习和深入学习&#xff0c;人工智能已经发展得更加先进和通用。 生成式预训练 Transformer (GPT) by OpenAI 已特别值得注意。每…

微服务远程调用 RestTemplate

Spring给我们提供了一个RestTemplate的API&#xff0c;可以方便的实现Http请求的发送。 同步客户端执行HTTP请求&#xff0c;在底层HTTP客户端库(如JDK HttpURLConnection、Apache HttpComponents等)上公开一个简单的模板方法API。RestTemplate通过HTTP方法为常见场景提供了模…

Algoriddim djay Pro Ai for Mac:AI引领,混音新篇章

当AI遇上音乐&#xff0c;会碰撞出怎样的火花&#xff1f;Algoriddim djay Pro Ai for Mac给出了答案。这款专业的DJ混音软件&#xff0c;以AI为引擎&#xff0c;引领我们进入混音的新篇章。 djay Pro Ai for Mac的智能混音功能&#xff0c;让每一位DJ都能感受到前所未有的创作…