# 1. 什么是SpringBoot的自动配置
SpringBoot自动配置,英文是Auto-Configuration:
- 是指基于你引入的依赖jar包,对SpringBoot应用进行自动配置
- 它为SpringBoot框架的"开箱即用"提供了基础支撑
# 2.自动配置和自动装配的区别
自动配置是:Auto-Configuration针对是Spring中的配置类,自动装配Autowire针对的是Spring的依赖注入。
# 3.术语“配置类”--Configuration class
- 广义的配置类:被注解@Component直接或间接修饰的某个类,就是我们常说的Spring组件,其中包含了@Configuration类
- 狭义的"配置类":特指被注解@Configuration所修饰的类,又称为@Configuration类
# 4.SpringBoot简化版本启动流程
# SpringBoot加载配置类的流程
# 注解@ComponectScan
@ComponentScan,是来自Spring框架的一个注解
- 它的作用是对制定的package进行扫描,找到其中符合条件的类,默认是搜索被@Component修饰的配置类
- 通过属性backPackages或basePackageClasses,来制定要进行扫描的package,来制定要进行扫描的package
- 如果未制定package,则默认扫描当前@ComponectScan所修饰的类所在的package
# @Import,是来自Spring框架的一个注解:
- 它的作用是提供了一种显示地从其它地方加载配置类的方式,这样可以避免使用性能较差的组件扫描(ComponectScan)
- 支持导入:
- 普通类(这里的"普通",是相对于随后的两个接口而言的);
- 接口ImportSelecter的实现(选择器类)类
- 可以通过@Import导入一个接口ImportSelector的实现类。接口ImportSelector中有一个selectImports方法,它的返回值是一个字符串数组,数组中的每个元素分别代表一个将被导入的配置类的全限定名。
- 接口ImportBeanDefinitionRegistrar的实现(注册器)
- 可以通过@Import导入一个接口ImportBeanDefinitionRegistrar的实现类。通过它,我们可以手动将多个BeanDefinition注册到Ioc容器中,从而实现个性化的定制。利用该特性我们可以给IOC容器动态地导入多个BeanDefinition
# SpringBoot使用那种方式来加载SpringBoot的自动配置类
- 首先排除注解@ComponentScan。因为使用它很不方便,开发人员需要记住每个第三方jar包中的package名称,然后把它们写到应用程序中。
- 在排除掉@Import中的接口ImportBeanDefinitionRegistrar的实现,BeanDefinition注册器的设计目标是对@Bean方法的一个补充,从名字就可以看出,它针对的是BeanDefinition层面的。
- 综上可以看出接口ImportSelecter的实现(选择器类)类是非常适合做Spirngboot的自动配置的
- 得出一个结论,Springboot使用@Import导入一个ImportSelector从而实现自动配置的功能
# 从源码来分析
我们都知道SpringBoot启动核心就是@SpringbootApplication,而SpringBootApplication中重要的注解就是下面这三个。
@EnableAutoConfiguration注解的含义就是开启自动配置的功能。
得到@SpringBoot的结构图
从图中我们得知
- 首先@SpringBootApplication修饰的类,也会被@Configuration间接修饰,即原配置类。
- 其次SpringBoot框架会对原配置类所在的package进行组件扫描(@ComponentScan)--现在大家知道为什么启动类所在的位置了吧
- 最终SpringBoot框架最终会导入AutoCOnfigurationImportSelector.class的选择器来实现自动配置。
# 如何实现类AutoConfigurationImportSelector
- SpringFactories机制
- Java SPI机制的延伸和扩展
- Spring框架的基础机制,在Spring以及SpringBoot源码中到处可见
- 可以基于它来实现SpringBoot的自动配置功能
- 它的核心逻辑是从classpath中读取到所有Jar包中的配置文件META-IF/spirng.factories然后根据指定的key从配置文件中解析出对应的value的值。
- 通过类SpringFactoriesLoader,返回一个类名的集合,可以根据实习需求对这些类名进行下一步的处理。
# 类AutoConfigurationImportSelector的关键源码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// SpringBoot的自动配置的入口方法
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 1.获取annotationMetadata的注解@EnableAutoConfiguration的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 从资源文件spring.factories中获取EnableAutoConfiguration对应的所有的类。
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
// 3.通过在注解@EnableAutoConfiguration设置exclude的相关属性,可以排除制定的自动配置类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//4 根据@conditional来判断是否需要排除某些自动配置类。
configurations = this.getConfigurationClassFilter().filter(configurations);
// 5 触发AutoConfiguration导入的相关事件。
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
主要看一下getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//通过SpringFactories机制,从配置文件Spring.factories中找出所有的自动配置类。
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;
}
从配置文件获取自动配置类时,使用的key是类EnableAutoConfiguraton的全限定名
@Conditional是来自Spring框架的一个注解
- 它的作用是实现:只有在特定条件满足时,才会向IOC容器注册指定的组件。
- 我们可以将@Conditonal理解为某种IF语句
总结一下自动配置的过程
# @ComponentScan和@EnableAutoConfiguration的区别
相同点:两者都可以将带有@Component@Service等注解的对象加入到ioc容器中。
不同点:
- 两者虽然都能将带有注解的对象放入ioc容器中,但是它们扫描的范围是不一样的。@ComponentScan扫描的范围默认是它所在的包以及子包中所有带有注解的对象,@EnableAutoConfiguration扫描的范围默认是它所在类。
- 它们作用的对象不一样,@EnableAutoConfiguration除了扫描本类带有的注解外,还会借助@Import的支持,收集和注册依赖包中相关的bean定义,将这些bean注入到ioc容器中,在springboot中注入的bean有两部分组成,一部分是自己在代码中写的标注有@Controller,@service,@Respority等注解的业务bean,这一部分bean就由@ComponentScan将它们加入到ioc容器中,还有一部分是springboot自带的相关bean,可以将这部分bean看成是工具bean,这部分bean就是由@EnableAutoConfiguration负责加入到容器中。
- @EnableAutoConfiguration可以单独启动springboot项目,而@ComponentScan是不
结论:Component注解可以将我们自定义bean加载到我们的ioc容器,而我们的系统级别的组件加载则由我们EnableAutoConfiguration注解来进行!