从源码中理解Spring Boot自动装配原理
个人博客:槿苏的知识铺
一、什么是自动装配
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar包来说,只需要按照SpringBoot定义的标准,就能将自己的功能装配到SpringBoot中。
二、自动装配的实现原理
自动装配的实现,离不开SpringBootApplication这个核心注解。查看这个注解的源码,我们会发现在SpringBootApplication注解上,存在着几个注解,其中SpringBootConfiguration、EnableAutoConfiguration、ComponentScan这三个注解是需要我们注意的。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
(1) ComponentScan
扫描被
@Component 、@Service注解的bean,该注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如SpringBootApplication注解源码所示,容器中将排除TypeExcludeFilterh和AutoConfigurationExcludeFilter。
(2) EnableAutoConfiguration
启用 SpringBoot 的自动配置机制
(3) Configuration
允许在上下文中注册额外的 bean 或导入其他配置类
2.1 EnableAutoConfiguration详解
@EnableAutoConfiguration是实现自动装配的重要注解,在这个注解上存在以下两个注解:AutoConfigurationPackage、Import。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
2.1.1 AutoConfigurationPackage
表示对于标注该注解的类的包,应当使用
AutoConfigurationPackages注册。实质上,它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
}
查看
AutoConfigurationPackages中的Registrar这个类的源码,在Registrar#registerBeanDefinitions方法中有这样一句代码new PackageImport(metadata).getPackageName(),查看PackageImport的构造器后不难发现,这里获取的是StandardAnnotationMetadata这个实例所在的包名。
/**
* metadata: 实际上是 StandardAnnotationMetadata 实例。
* metadata#getClassName(): 获取标注 @AutoConfigurationPackage 注解的类的全限定名。
* ClassUtils.getPackageName(…): 获取其所在包。
*/
PackageImport(AnnotationMetadata metadata) {
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
}
此时再回去看
Registrar#registerBeanDefinitions中调用的AutoConfigurationPackages#register方法
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// BEAN:AutoConfigurationPackages类的全限定名
// 此时判断BeanDefinitionRegistry中是否存在以BEAN作为beanName的BeanDefinition对象
// 如果不存在,走else方法,构造了一个BackPackages实例,进行注册
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
2.1.2 Import(AutoConfigurationImportSelector.class)
它是利用
AutoConfigurationImportSelector机制再来给容器中批量导入一些配置东西的,接下来带大家了解究竟导入了哪些内容。
/**
* AutoConfigurationImportSelector类中存在一个叫selectImports的方法,就是我们到底要向容器中导入哪些
* 内容,都会在这里进行扫描并导入。
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断EnableAutoConfiguration是否开启默认开启true
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 1.加载META-INF/spring-autoconfigure-metadata.properties 文件
// 2.从中获取所有符合条件的支持自动装配的类
// 自动配置类全名.条件=条件的值
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 获取AutoConfigurationEntry
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
接下来重点看
getAutoConfigurationEntry(annotationMetadata)方法,利用这个方法向容器中批量导入一些默认支持自动配置的类,当你理解了这部分内容之后,就基本了解了Spring Boot是如何进行自动装配的,废话不多说,让我们进入正题。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 判断EnableAutoConfiguration是否开启默认开启true
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 调用getCandidateConfigurations(annotationMetadata, attributes),利用loadSpringFactories(ClassLoader classLoader)加载当前系统所有的META-INF/spring.factories文件,得到默认支持的自动配置的类的列表
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复的 configuration
configurations = removeDuplicates(configurations);
// 获取到SpringBootApplication上exclude和excludeName配置的需要排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查configurations是否含有exclusions中的类
checkExcludedClasses(configurations, exclusions);
// 将exclusions中的类从configurations中排除
configurations.removeAll(exclusions);
// 对所有候选的自动配置类进行筛选,
// 比如ConditionalOnProperty 当属性存在时
// ConditionalOnClass 当class存在
// ConditionalOnMissingClass 当这个clas不存在时才去配置
// 过滤器
configurations = getConfigurationClassFilter().filter(configurations);
// 将自动配置的类,导入事件监听器,并触发fireAutoConfigurationImportEvents事件
// 加载META-INF\spring.factories中的AutoConfigurationImportListener
fireAutoConfigurationImportEvents(configurations, exclusions);
// 创建AutoConfigurationEntry对象
return new AutoConfigurationEntry(configurations, exclusions);
}
从源码中理解Spring Boot自动装配原理的更多相关文章
- Spring Boot 自动装配原理
Spring Boot 自动装配原理 Spring Boot 在启动之前还有一系列的准备工作,比如:推断 web 应用类型,设置初始化器,设置监听器,启动各种监听器,准备环境,创建 applicati ...
- Spring Boot系列(二):Spring Boot自动装配原理解析
一.Spring Boot整合第三方组件(Redis为例) 1.加依赖 <!--redis--> <dependency> <groupId>org.springf ...
- Spring Boot自动装配原理源码分析
1.环境准备 使用IDEA Spring Initializr快速创建一个Spring Boot项目 添加一个Controller类 @RestController public class Hell ...
- Spring Boot自动配置原理(转)
第3章 Spring Boot自动配置原理 3.1 SpringBoot的核心组件模块 首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量: 我们cd到spring-boot- ...
- Spring Boot自动装配
前言 一些朋友问我怎么读源码,这篇文章结合我看源码时候一些思路给大家聊聊,我主要从这三个方向出发: 确定目标,这个目标要是一个具体,不要一上来我要看懂Spring,这是不可能的,目标要这么来定,比如看 ...
- Spring Boot 自动装配(二)
目录 目录 前言 1.起源 2.Spring Boot 自动装配实现 2.1.@EnableAutoConfiguration 实现 2.1.1. 获取默认包扫描路径 2.1.2.获取自动装配的组件 ...
- Spring Boot 自动装配流程
Spring Boot 自动装配流程 本文以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的自动装配流程. Spring Boot 发现自动配置类 这 ...
- Spring Boot自动配置原理与实践(二)
前言 在之前的博文(Spring Boot自动配置原理与实践(一))中,已经介绍了Spring boot的自动配置的相关原理与概念,本篇主要是对自动配置的实践,即自定义Starter,对原理与概念加深 ...
- Spring Boot自动配置原理、实战
Spring Boot自动配置原理 Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射. org.s ...
随机推荐
- Windows 启动过程
引言 启动过程是我们了解操作系统的第一个环节.了解 Windows 的启动过程,可以帮助我们解决一些启动的问题,也能帮助我们了解 Windows 的整体结构. 以下内容将分为[加载内核].[内核初始化 ...
- 从svn下载项目后出现 Error:java: Compilation failed: internaljava compiler error 解决办法
原因:出现这个问题的话,主要是因为导入的项目JDK版本与自己的不匹配. 解决方法如下: 最后,酱紫
- idea启动java Maven项目,出现" java: 程序包xxxx不存在"
解决办法如下:将idea的构建和运行托管到maven下面
- [开源精品] .NET Redis Client 又多了一个选择,还在被 StackExchange.Redis Timeout 问题困扰吗?
前言 .NET 下 RedisClient SDK 选择挺多,国人常用免费的有 StackExchange.Redis/CSRedis/Newlife.Redis,收费的有 ServiceStack. ...
- 攻防世界MISC—进阶区11-20
11.János-the-Ripper 得到未知类型的文件,010 Editor打开后看到pk,得知是真加密的zip文件. 密码在文件中没有提示,根据题目名字,János-the-Ripper Ján ...
- logback-spring 集成 ELK、kafka的配置
pom.xml <dependency> <groupId>com.github.danielwegener</groupId> <artifactId> ...
- Template -「整体二分」
写的简单.主要是留给自己做复习资料. 「BZOJ1901」Dynamic Rankings. 给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\),需要支持两种操作: Q ...
- 初学者入门:使用WordPress搭建一个专属自己的博客
体验简介 阿里云云起实验室提供相关实验资源,点击前往 场景将提供一台基础环境为CentOS 的ECS(云服务器)实例,这台服务器上已经内置LAMP环境.我们将会在这台服务器上安装 WordPress ...
- python 线程理解
简介 一个应用程序由多个进程组成,一个进程有多个线程,一个线程则是操作系统调度的最小单位,当应用程序运行时,操作系统根据优先级和时间片调度线程(决定此时此刻执行哪个线程). python的线程 pyt ...
- PostgreSQL定时备份
# PostgreSQL定时备份 # 版本:PostgreSQL14.0 # 一.如果你在pg_hba.conf中设定了密码登录,那么你就需要设置一个默认密码共psql登录的时候跳过验证.如果是roo ...