从源码中理解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 ...
随机推荐
- MAC帧的格式&&wireshark分析MAC帧
MAC帧的格式 MAC帧较为简单,由五个字段组成 目的地址:6字节 源地址:6字节 类型字段:2字节,用来标志上一层使用的是什么协议,以便把收到的MAC地址帧的数据交给上一层的这个协议. 数据字段:其 ...
- ppt/word公式LaTeX环境配置
PPT使用Latex说明 一.下载IguanaTex_v1_57 http://www.jonathanleroux.org/software/iguanatex/download.html Inst ...
- NC16783 [NOIP1998]拼数
NC16783 [NOIP1998]拼数 题目 题目描述 设有 \(n\) 个正整数(\(n ≤ 20\)),将它们联接成一排,组成一个最大的多位整数. 例如:\(n=3\) 时,\(3\) 个整数 ...
- Training a classifier
你已经学习了如何定义神经网络,计算损失和执行网络权重的更新. 现在你或许在思考. What about data? 通常当你需要处理图像,文本,音频,视频数据,你能够使用标准的python包将数据加载 ...
- NC16884 [NOI2001]食物链
题目链接 题目 题目描述 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到 ...
- C++20 以 Bazel & Clang 开始
C++20 如何以 Bazel & Clang 进行构建呢? 本文将介绍: Bazel 构建系统的安装 LLVM 编译系统的安装 Clang is an "LLVM native&q ...
- CTCLoss如何使用
CTCLoss如何使用 目录 CTCLoss如何使用 什么是CTC 架构介绍 一个简单的例子 CTC计算的推导 总概率\(p(z|x)\) 路径的含义 路径概率\(p(\pi|x)\) 什么是\(\m ...
- Aborted wiping of zfs_member.1 existing signature left on the device.Aborting pvcreate on /dev/sda
看意思是有什么签名在上面,创建不了逻辑卷,确认数据备份的情况下执行对应的下面命令: wipefs --all --force /dev/sdax
- C#Lambda表达式演变和Linq的深度解析
Lambda 一.Lambda的演变 Lambda的演变,从下面的类中可以看出,.Net Framwork1.0时还是用方法实例化委托的,2.0的时候出现了匿名方法,3.0的时候出现了Lambda. ...
- 2500-使用MyBatis操作MySQL进行批量更新的注意事项
原则上一条SQL只更新一条数据库操作,但有时需要批量操作数据,特别是一些DML语句,在操作数据库时,数据库会报出异常,不允许混合语句,此时需要额外配置进行兼容. 例如: Caused by: com. ...