SpringBoot启动的时候做了什么(1)
SpringBoot启动的时候做了什么(2)
SpringBoot的自动配置是一个很重要的特点,它可以大量的减少我们对相关框架的配置工作。之前两篇文章讲了SpringBoot的main方法内部逻辑,接下来开始学习SpringBoot的自动配置。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration //标明该类为配置类
@EnableAutoConfiguration //启动自动装配功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@SpringBootApplication
注解是个组合注解,其上有三个关键注解:
@SpringBootConfiguration
其实就是被Spring的@Configuration
修饰的,两者功能一致,只是在SpringBoot中换了个名字而已。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
可以看到,要分析@AutoConfigurationPackage
的作用,关键入口是AutoConfigurationPackages.Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
Registrar
是AutoConfigurationPackages
的一个静态内部类,再看register
方法:
private static final String BEAN = AutoConfigurationPackages.class.getName();
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//BEAN 就是当前类,项目启动后第一次解析,肯定不会进第一个if分支
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
BEAN 就是当前类,项目启动后第一次解析,肯定不会进第一个if分支。
第二个分支里边的逻辑就很简单了,就是注册一个BeanDefinition
。
关于register
方法的第一个参数BeanDefinitionRegistry
之前写过文章,点我。⬅️👀
回到调用register
的地方🔙🔙🔙
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
那接下来其实我们只要搞清楚register
的第二个参数,就可以明白AutoConfigurationPackage
做了什么。
别闹了,不会还要看代码吧,直接debug不来的痛快么😂
debug结果可以看到new PackageImports(metadata).getPackageNames()
其实就是获取本项目启动类所在的项目路径。
beanDefinitionMap
中也看到了当前启动类项目路径已被注册。
总结
@AutoConfigurationPackage
作用是将启动类所在路径注册到Spring。
这里需要知道@Import
是做什么的,此注解不做展开。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
!isEnabled(annotationMetadata)
:判断当前项目是否开启自动装配,是通过一个配置实现的,默认打开。很简单就贴代码了。
重点看getAutoConfigurationEntry(annotationMetadata)
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
@EnableAutoConfiguration
有两个属性:
Class<?>[] exclude() default {};
String[] excludeName() default {};
getAttributes(annotationMetadata)
这个方法的作用就是获取这两个属性的值。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
先看SpringFactoriesLoader.loadFactoryNames
可以看到这里就是从META-INF/spring.factories
中获取所有的EnableAutoConfiguration
实现。
再看ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
这个方法就是从spring-boot-autoconfigure
中获取springboot目前支持的各框架的配置类。
简单看几个:
后边的就没啥必要看了,就是做一下去重、剔除之前exclued配置的。
这个下篇再说吧,要下班了
META-INF/spring.factories
中获取所有配置类,中间用到了SPI
机制。