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的定义列表