Spring Boot自动配置与run执行流程
1、自动配置
自动配置
:根据添加的jar包依赖,会自动将一些配置类的bean注册到IoC容器中,可以在需要的地方使用@Autowired
或者@Resuorce
等注解来使用它。
Spring Boot应用的启动入口是@SpringBootApplication
注解标注类中的main()方法。
1.1、@SpringBootApplication
SpringBootAppliaction注解源码中核心代码如下:
@Target(ElementType.TYPE) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解
@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) //注解扫描
public @interface SpringBootApplication {
// 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {
};
// 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {
};
// 指定扫描包,参数是包名的字符串数组。
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {
};
// 扫描特定的包,参数类似是Class类型数组。
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {
};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
从源码中可以看出,@SpringBootApplication注解是一个组合注解,前面四个是注解的元数据信息,我们主要看@SpringBootConfiguration、@EnableAutoConfiguration、@ComponetScan
三个核心注解
1.2、@SpringBootConfiguration
@SpringBootConfiguration
:SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类
@SpringBootConfiguration注解源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
从上述源码可以看出,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解是Spring框架提供的,表示当前类为一个配置类,并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是配Spring Boot进行的重新封装命名而已
1.3、@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class) // Spring的底层注解@Import,给容器中导入一个组件;
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 返回不会被导入到Spring容器中的类
Class<?>[] exclude() default {
};
// 返回不会被导入到Spring容器中的类名
String[] excludeName() default {
};
}
Spring中有很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的Bean,并加载到IoC容器中。
@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。
1.3.1、@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// Spring的底层注解@Import,给容器中导入一个组件;
// 导入的组件是AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
@AutoConfigurationPackage
:自动配置包,它也是个组合注解,其中最重要的注解就是@Import(AutoConfigurationPackages.Registrar.class)
,它是Spring框架底层的注解,它的作用就是给容器中导入某个组件类,本处就是将Registrar这个组件类导入到容器中,其中Registrar类中的registerBeanDefinitions方法如下:
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 将注解标注的元信息传入,获取到相应的包名
register(registry, new PackageImport(metadata).getPackageName());
}
new PackageImport(metadata).getPackageName()
返回了当前启动类所在包名,再看register方法
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解
// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包
if (registry.containsBeanDefinition(BEAN)) {
// 如果该bean已经注册,则将要注册包名称添加进去
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
//如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
AutoConfigurationPackages.Registrar
类就做了一个事,注册一个Bean,这个Bean就是com.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages
,它有一个参数,这个参数是使用了@AutoconfigurationPackage
这个注解的类所在的包路径,保存自动配置类以供之后的使用,比如给JPA entity
扫描器用来扫描开发人员通过注解@Entity
定义的entity类。
1.3.2、@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class)
将AutoConfigurationImportSelector
这个类导入到Spring容器中,AutoConfigurationImportSelector
可以帮助SpringBoot应用将所有符条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器中。
可以看到AutoConfigurationImportSelector
重点是实现了DeferredImportSelector
接口又继承了ImportSelector
接口。它还是实现了很多其他的Aware
接口,分别表示在某个时机会被回调。
1.3.2.1、自动配置实现逻辑的入口方法
跟自动配置逻辑相关的入口方法在DeferredImportSelectorGrouping
类的getImports
方法处,因此就从DeferredImportSelectorGrouping
类的getImports
方法开始分析SpringBoot的自动配置源码。
如下为DeferredImportSelectorGrouping
类的getImports
方法源码:
public Iterable<Group.Entry> getImports() {
// 遍历DeferrdImportSelectorHolder对象集合deferredImports,deferredImports集合装了各种ImportSelectory,在此处装的是AutoConfigurationImportSelector
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
//[1] 利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
//[2] 经过上面的处理,然后再进行选择导入哪些配置类
return this.group.selectImports();
}
如上代码,注释[1]处,自动配置的相关的绝大部分逻辑在此处,即调用AutoConfigurationGroup
对象的process
方法,传入的AutoConfigurationImportSelector对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类。
注:
AutoConfigurationGroup:是AutoConfigurationImportSelector的内部类,主要用来处理自动配置相关逻辑,拥有process和selectImports方法,然后拥有entries和autoConfigurationEntries集合属性,这两个集合分别存储被处理后的符合条件的自动配置类。
AutoConfigurationImportSelector:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类。
metadata:标注在SpringBoot启动类上的@SpringBootApplication注解元数据。
注释[2] 处的this.group.selectImports的方法主要是针对前面process方法处理后的自动配置类再进一步筛选导入
再进入到AutoConfigurationImportSelector$AutoConfigurationGroup的pross方法:
于第一行断点调试,可以看到与自动配置逻辑相关的入口方法在process方法中
1.3.2.2、自动配置的主要逻辑
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 【1】,调用getAutoConfigurationEntry方法得到自动配置类放入autoConfigurationEntry对象中
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
// 【2】,又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 【3】,遍历刚获取的自动配置类
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
// 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
在【1】处调用了getAutoConfigurationEntry
方法,这个方法主要是用来获取自动配置相关类,承担了自动配置的主要逻辑。代码如下:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 获取是否有配置spring.boot.enableautoconfiguration属性