Spring Boot 命名配置很少,却可以做到和其他配置复杂的框架相同的功能工作,从源码来看是怎么做到的。

我这里使用的Spring Boot版本是 2.0.1.RELEASE

Spring Boot最重要的注解: @SpringBootApplication

打开它:

其中的几个注解:

@SpringBootConfiguration   标注这个类是一个配置类,类似Spring的@Configuration

@EnableAutoConfiguration   开启自动配置

@ComponentScan   开启组件扫描

打开@SpringBootConfiguration:

可以看作是springboot将spring的Configuration注解进行一个包装

@EnableAutoConfiguration

@AutoConfigurationPackage  自动扫描包的注解

@Import({AutoConfigurationImportSelector.class})  引入组件

这个@AutoConfigurationPackage注解就是扫描跟主配置类同级目录以及子目录下的包,这也是什么Spring Boot其他的包必须在主配置类同级或者子目录以下的原因。

这里最关键的就是@Import注解向容器里导入了什么组件,导入的AutoConfigurationImportSelector.class,打开这个类,注意:在Spring Boot1.5版本中这里导入的类是:EnableAutoConfigurationImportSelector,而EnableAutoConfigurationImportSelector是继承自AutoConfigurationImportSelector的。

里面有一个  selectImports()  方法很重要。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}

getCandidateConfigurations() 方法

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

SpringFactoryLoader去加载一些组件的名字,看加载那些组件的名字,继续点开loadFactoryNames方法

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

loadSpringFactories方法:

   private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
} cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}

标黄部分将url转何曾properties,通过传入的键获取值,在将值切割成一个个字符串,转成Array存到result里。

META-INF/spring.factories:

找到其中的

# Auto Configure

可以看到:

上图里面这么多的xxxAutoConfiguration就是我们的这么久得出的结果,最终就是加载这么多的类的全路径,然后springboot内部就是实例化这些类并加载到容器里面,完成springboot应用启动时的自动配置。

通过断点我们可以看到:

总结一下流程:

Spring Boot启动 >

@SpringBootApplication   >

@EnableAutoConfiguration   >

@AutoConfigurationPackage + @Import({AutoConfigurationImportSelector.class})   >

public String[] selectImports() {List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);}   >

List<String> configurations = SpringFactoriesLoader.loadFactoryNames();   >

classLoader.getResources("META-INF/spring.factories")   >

org/springframework/boot/spring-boot-autoconfigure/2.0.1.RELEASE/spring-boot-autoconfigure-2.0.1.RELEASE.jar!/META-INF/spring.factories   >

spring.factories:各种xxxxAutoConfiguration的全类名

  

从源码看Spring Boot 2.0.1的更多相关文章

  1. 从源码看Spring Security之采坑笔记(Spring Boot篇)

    一:唠嗑 鼓捣了两天的Spring Security,踩了不少坑.如果你在学Spring Security,恰好又是使用的Spring Boot,那么给我点个赞吧!这篇博客将会让你了解Spring S ...

  2. 源码解读 Spring Boot Profiles

    前言 上文<一文掌握 Spring Boot Profiles> 是对 Spring Boot Profiles 的介绍和使用,因此本文将从源码角度探究 Spring Boot Profi ...

  3. 头秃了,二十三张图带你从源码了解Spring Boot 的启动流程~

    持续原创输出,点击上方蓝字关注我 目录 前言 源码版本 从哪入手? 源码如何切分? 如何创建SpringApplication? 设置应用类型 设置初始化器(Initializer) 设置监听器(Li ...

  4. 从源码理解Spring原理,并用代码实现简易Spring框架

    前言(本文为原创,转载请注明出处) 个人之前对于框架的学习,就停留在配置,使用阶段.说实话过段时间就会忘得荡然无存.也不知道框架的运行逻辑,就是知道添加个注解,就可以用了. 由于实习,时间比较多,也感 ...

  5. 手把手教你调试SpringBoot启动 IoC容器初始化源码,spring如何解决循环依赖

    授人以鱼不如授人以渔,首先声明这篇文章并没有过多的总结和结论,主要内容是教大家如何一步一步自己手动debug调试源码,然后总结spring如何解决的循环依赖,最后,操作很简单,有手就行. 本次调试 是 ...

  6. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

  7. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  8. spring boot 2.0 源码分析(五)

    在上一篇文章中我们详细分析了spring boot是如何准备上下文环境的,今天我们来看一下run函数剩余的内容.还是先把run函数贴出来: /** * Run the Spring applicati ...

  9. spring boot 2.0 源码分析(二)

    在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /** * Run the ...

随机推荐

  1. Python 入门基础20 --面向对象_继承、组合

    今日内容 组合:自定义类的对象作为类的属性 继承:父类与子类.多继承 1.组合 将自定义类的对象作为类的属性 class Teacher: def __init__(self, name): self ...

  2. java读取视频文件时长

    1.下载jar包:http://www.sauronsoftware.it/projects/jave/index.php 2.上代码 @RequestMapping(value = "am ...

  3. 【Linux】VMware虚拟机中如何配置静态IP

    此处详解如何在VMware虚拟机中配置静态IP. 上图是一个草图,大致说明下VMware网络情况.网段指某个IP范围,例如一栋小区楼和另一栋小区楼就不是一个网段 如果你在A小区,你女朋友在B小区,你想 ...

  4. sublime3添加python编译系统

    好记性不如烂笔头 为sublime3添加python编译系统,这里使用的anonconda2中的python.exe(即python2.7版本) 步骤: (1)打开sublime,打开“工具-> ...

  5. git获取内核源码的方法

    [转]http://www.360doc.com/content/17/0410/16/23107068_644444795.shtml 1. 前言 本文主要讲述ubuntu下通过git下载linux ...

  6. jdk8系列三、jdk8之stream原理及流创建、排序、转换等处理

    一.为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX ...

  7. sipML5聊天功能实现

    一.环境说明:在阅读sipML5的API文档时,发现它具有聊天的功能,于是在sipML5的源码中进行设定,实现了注册之后可以英文聊天(中文聊天需要在FreeSWITCh中进行设定). 二.具体配置: ...

  8. Oracle11默认用户名和密码

    安装Oracle时,若没有为下列用户重设密码,则其默认密码如下: 用户名 / 密码                      登录身份                              说明s ...

  9. WebMvcConfigurerAdapter已过时

    Spring Boot2.0的版本(创建的时候自动选择的这个版本),然后编译器告诉我WebMvcConfigurerAdapter已过时了 @Deprecated public abstract cl ...

  10. InterruptedException 异常

    当一个方法后面声明可能会抛出InterruptedException 异常时,说明该方法是可能会花一点时间,但是可以取消的方法. 抛InterruptedException的代表方法有: 1. jav ...