Spring Boot自动装配原理源码分析

1.环境准备

使用IDEA Spring Initializr快速创建一个Spring Boot项目

添加一个Controller类

@RestController
public class HelloController { @RequestMapping("hello")
public String hello() {
return "hello";
}
}

主配置类如下

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

2.注解分析

Spring Boot规定,项目的主配置类必须放在最外层包,也就是说,所有的类都必须放在主配置类的同级包或者子包里,这么做的用意是什么?我们点开@SpringBootApplication注解慢慢分析(下面代码中省略元注解)...

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

@SpringBootApplication内部标注了三个注解:

  • @SpringBootConfiguration

    进入源码中可以看见,@SpringBootConfiguration其实就是Spring中的@Configuration,用于标注配置类

    @Configuration
    public @interface SpringBootConfiguration {
  • @ComponentScan

    这个注解也是Spring中的,它用来将指定包需要装配的组件注册到容器中

  • @EnableAutoConfiguration

    接下来才是今天的重头戏,Spring Boot自动配置的主角!

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {

3.自动装配的主角

​ 进入@EnableAutoConfiguration源码你会发现这个注解中标注了两个注解@AutoConfigurationPackage@Import

(1)、@AutoConfigurationPackage

点进该注解

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

在点进Register,这是一个静态内部类

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

  @Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
} @Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
} }

我们在第一个方法处打一个断点debug一下,发现new PackageImport(metadata).getPackageName()的结果其实就是一个包名,这时我们很容易的可以想到,这个包就是Spring Boot主配置类所在的包

再看一眼metadata,果然,就是主配置类

因此,这个注解的作用就是将主配置类所在的包作为自动配置包进行管理

(2)、@Import(AutoConfigurationImportSelector.class)

@Import的作用就是导入一个类到IOC容器,我们先来看一下导入的这个类:自动配置导入选择器

源码里有一个方法selectImports,选择导入

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

在点进getAutoConfigurationEntry,获取自动配置类

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
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 = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations方法处打一个断点,发现configurations的结果是所有的xxxAtuoConfiguration类,一共124个,请记住这个数字。

那么这些自动配置类是如何获取的呢,从哪里获取的呢?

我们继续点进getCandidateConfigurations,获取候选的配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations =
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "... ...");
return configurations;
}

继续点,loadFactoryNames,加载工厂名,方法所在类中有一个常量FACTORIES_RESOURCE_LOCATION,看代码可以清晰的看到,这方法加载classpath下的所有jar包的META-INF/spring.factories文件,结果用一个HashMap存储

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
} private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
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();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

打开spring-boot-autoconfigure-2.2.4.RELEASE.jar/META-INF/spring.factories,文件部分类容如下,你可以点进去看看第22~145行,确实是124个全类名

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
... ...

4.类的加载时机

加载了这么多类我们又不一定全都用得到,设计师肯定会想办法让类在我们需要的时候才生效,我们随便点进一个类,可以看到一片飘红,因为我们并没有引入RabbitMQ相关依赖,再看一个注解ConditionalOnClass,意思就是存在某个指定的类才生效,类似的注解还有很多,都是@ConditionaOn xxx,在一定条件下类才会生效。

由于引入了web模块,WebMvcAutoConfiguration正常显示

Spring Boot自动装配原理源码分析的更多相关文章

  1. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  2. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  3. Spring Boot自动装配

    前言 一些朋友问我怎么读源码,这篇文章结合我看源码时候一些思路给大家聊聊,我主要从这三个方向出发: 确定目标,这个目标要是一个具体,不要一上来我要看懂Spring,这是不可能的,目标要这么来定,比如看 ...

  4. Spring Boot 自动装配流程

    Spring Boot 自动装配流程 本文以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的自动装配流程. Spring Boot 发现自动配置类 这 ...

  5. Spring Boot系列(二):Spring Boot自动装配原理解析

    一.Spring Boot整合第三方组件(Redis为例) 1.加依赖 <!--redis--> <dependency> <groupId>org.springf ...

  6. Spring Boot 自动装配(二)

    目录 目录 前言 1.起源 2.Spring Boot 自动装配实现 2.1.@EnableAutoConfiguration 实现 2.1.1. 获取默认包扫描路径 2.1.2.获取自动装配的组件 ...

  7. Spring Boot 自动装配原理

    Spring Boot 自动装配原理 Spring Boot 在启动之前还有一系列的准备工作,比如:推断 web 应用类型,设置初始化器,设置监听器,启动各种监听器,准备环境,创建 applicati ...

  8. SpringBoot自动配置原理源码级别分析

    SpringBoot自动配置原理 前言 后面还会讲到SpringBoot自动配置原理,会主要讲解@EnableAutoConfiguratuon注解帮助我们做了什么事情,是如何自动把自动配置类扫描到容 ...

  9. 从源码中理解Spring Boot自动装配原理

    个人博客:槿苏的知识铺 一.什么是自动装配 SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factori ...

随机推荐

  1. 查看磁盘型号和内存及raid信息

    1.查看磁盘型号 工具:smartmontools #smartctl --help #smartctl --all /dev/sda -d megarid,1 (第一块磁盘的信息) #smartct ...

  2. Java环境准备

    电脑重装系统了,所以需要重新配置环境变量. 首先必备工具:jak.eclipse.maven.tomcat 首先配置Java运行环境. 在系统环境变量中新建变量JAVA_HOME:jdk所在的路径,P ...

  3. Go Goosy Disk Docker Port Provisioners(GDP)

    小伙伴们,她们中出了一个叛徒,他是谁?是谁?是谁? 由一则口口相传的故事开始吧: 中午吃饭时间抽空小李跑到同座大楼的小张公司串门,小李是一名docker顾问熟称砖家,这间公司老板想挖小李,他盯了前台不 ...

  4. MySQL快速回顾:计算字段与函数

    9.1 计算字段 存储在数据库表中的数据一般不是应用程序所需要的格式.比如: 如果想要在一个字段中既显示公司名,又显示公式的地址,但这两个信息一般包含在不同的表列中. 城市.州和邮政编码存储在不同的列 ...

  5. 快速幂模板Super

    //求x^nint ans=1;while(n){ if(n&1) ans=ans*x; x*=x; n>>=1;} 快速幂就是快速算底数的n次幂.其时间复杂度为 O(logN), ...

  6. Appium自动化测试框架研究(2)——搭建IOS环境

    今天的文章讲iOS的Appium环境搭建. 对于iOS而言,只能在Mac笔记本上安装Appium,以及所需要的各种组件. 也许有人会问,能否在Windows系统上使用Appium测试iOS手机,这不就 ...

  7. django操作命令

    下载安装 pip3 install django==1.11.21 -i https://pypi.tuna.tsinghua.edu.cn/simple 创建项目 1.终端找到存放项目的文件夹,dj ...

  8. python 进程信号量

    1.概念 信号量和锁相似,锁同一时间只允许一个对象(进程)通过,信号量同一时间允许多个对象(进程)通过 2.应用场景 多线程,并规定数量 3.格式: 导入信号量模块 实例化信号量对象,可以规定信号量的 ...

  9. Django 连接mysql 踩过的坑

    1.创建数据库 2.在Django项目文件下的settings.py配置数据库 3.在Django项目__init__.py文件中,用pymysql代替MySqlDB import pymysql p ...

  10. Java单体应用 - Markdown - 01.简介

    原文地址:http://www.work100.net/training/monolithic-markdown.html 更多教程:光束云 - 免费课程 简介 序号 文内章节 视频 1 概述 2 特 ...