SpringBoot流行之前,程序员大多是用SSM框架
整合来进行WEB后端开发。这种方式非常麻烦,需要手动引入大量的包,还要配置很多XML文件,光是搭建环境就需要很久。
基于这种的SSM中xml配置的繁琐,后来衍生出SpringBoot。SpringBoot中的自动装载,大大简化了开发者对于配置的相关信息。
问题:什么是SpringBoot自动配置?
- 当spring容器启动后,一些自动配置类通过
@Conditional
注解自动装配的IOC容器中 - 不需要手动去注入,简化了开发,省去了繁琐的配置
- 自动配置的相关工作就在
@SpringBootApplication
这个注解上
我们来看一下@SpringBootApplication
这个注解。
@Target({ElementType.TYPE}) //注解的作用范围,用在类,接口,注解等上面
@Retention(RetentionPolicy.RUNTIME) //注解生命周期,runtime,保留在运行时期
@Documented //可以被文档化
@Inherited //可以被子类继承
@SpringBootConfiguration //里面是@Configuration属于配置类
@EnableAutoConfiguration //启动自动配置功能
//配置扫描包
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication
@SpringBootApplication 是一个复合注解,由几个核心的注解组成。
- @SpringBootConfiguration
- 里面是 @Configuration,代表是一个配置类,说明主程序类也是一个配置类
- @EnableAutoConfiguration
- @AutoConfigurationPackage 将指定的一个包下的所有组件导入到容器当中
- 在@AutoConfigurationPackage 注解中存在一个 @Import({Registrar.class}) 注解,自动配置包就是通过这个完成的。
- @ComponentScan
- 指定扫描哪些组件,默认是扫描主程序所在的包以及其子包
它的核心在于@EnableAutoConfiguration
这个注解,这里面是加载自动配置的类信息。
@EnableAutoConfiguration注解核心内容
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class) //通过import导入满足条件的bean,并加载到spring的ioc容器里面
public @interface EnableAutoConfiguration
@AutoConfigurationPackage注解核心内容
- Registrar的作用是扫描包,默认是把主类所在的包和子包里面全部类扫描进容器里面
- 所以为什么开发springboot项目需要把主类放到最外层目录,不然就对的注解类就找不到
@Import(AutoConfigurationPackages.Registrar.class) //把Registrar导入到spring容器里面
核心逻辑为这段逻辑,一会我们会断点进行调试。
//获取主程序所在的目录为位置,metadata是元注解信息
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
下面,我们来看一下@Import(AutoConfigurationImportSelector.class)
这个里面都做了哪些操作。其核心就是通过import导入满足条件的bean, 把springboot应用里面符合@Configuration的类,加载到spring的ioc容器里面
//用于实现动态注册Bean的功能,【批量】导入对象到容器里,根据条件动态地选择需要注册的Bean,并加入Spring容器
//实现ImportSelector接口,这个接口的selectImports方法会返回一个String数组,数组中的值就是要添加的组件的全类名
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());
}
ok,我们再来看一下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()
这里面核心逻辑就是去META-INF/spring.factories
这个文件中去拉取全部的配置信息。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
好的,接下来我们来调试走下源码流程。
ok,我们来总结一下,SpringBoot自动装载的全流程。
- 首先,加载一下元数据信息
- 获取需要自动装载的类的信息
- 判断是否启用了自动配置
- 获取候选自动配置类列表
- 获取需要排除的自动配置类列表
- 检查是否存在需要排除的自动配置类
- 将需要排除的类从自动配置类列表中移除
- 获取配置类过滤器,对候选自动配置类列表进行过滤
- 触发自动配置导入事件,并返回一个新的自动配置条目
- 注册Bean的定义列表