@SpringBootApplication
@SpringBootApplication
这个注解是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 {
}
我们可以看到里面有几个子注解,首先看看@SpringBootConfiguration
@SpringBootConfiguration
,这个注解标识了启动类也是个配置类。这个很好理解,启动类的底层帮我们做了很多默认的配置,这些配置都是starter以来里面定义好的(spring.factory下),所以启动类会做一些配置的东西
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@EnableAutoConfiguration
其次再看看@EnableAutoConfiguration,里面有@AutoConfigurationPackage和@Import()。@AutoConfigurationPackage主要是把启动类,类路径等元数据进行注册,这些注册的逻辑都在AutoConfigurationPackage的内部静态类Registrar下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
而Registrar类里面的核心方法registerBeanDefinitions就是注册元数据的地方。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
}
BeanDefinitionRegistry 是进行注册的注册器,AnnotationMetadata 的元数据,封装了启动类的各种信息。AutoConfigurationPackages.register方法就会将元数据进行注册,而new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()则会解析元数据获取类路径。这就是为什么SpringBoot只会扫描启动类位置及其子包下的注解。原因就是这里获取的就是启动类的包路径为初始路径。
我们再看看@Import(),@Import会注入类进入ioc容器,关键是这个类AutoConfigurationImportSelector有什么用?这个类是用来将各种starter的默认配置进行加载的,那么就清晰了,上述的Registrar是用来注册自己路径下的组件,而AutoConfigurationImportSelector是用来注册依赖的starter的组件。注册的关键方法是AutoConfigurationImportSelector下的selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
最关键的是AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry的获取,Entry,说明有些信息是键值对形式被获取的,我们进去看看
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
里面获取了List
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
那么就很明显了,SpringFactoriesLoader会在META-INF/spring.factories下找配置文件,里面定义了各种starter的配置类全包名。debug进去后在loadFactoryNames可以看到 Enumeration
小结
小结
通过注解,SpringBoot主要加载了启动类的各种元数据并注册,这是为了进行扫描配置我们自定义的配置类。其次通过自动配置扫描了META-INF/spring.factories下的文件获取sarter默认配置好的配置类进行配置(我们不需要额外配置,这就是约定)。
评论区