SpringBoot的@SpringBootApplication分析
@SpringBootApplication总览
上一篇,我们有提到run()方法调用了prepareContext()方法,而preparenContext就是会将run方法的primarySource即(run方法传的类,标注了SpringBootApplication的类)放到spring的BeanDefinition中,后续refresh的时候,springboot就会将该类实例化并放到spring容器中。

这里以我自己写的EurekaServerApplication为例子:
上面的load方法就是将箭头指向的EurekaServerApplication,放到context的beanDefinition中,后续refresh的时候,spring会将将其注入到容器中。
于是,这个类上的注解也会在此过程中生效,看看@Springboot 是有哪些注解组合而成的吧。

核心就是@SpringBootConfiguration和@EnableAutoConfiguration和@ComponentScan
@SpringBootConfiguration

它就是Configuration的包装,功能和Configuration一样,标识为一个配置类,可以在这个类里面写配置代码。
@ComponentScan
这个类是Spring的,用来指定Spring的扫描路径,不配置路径则扫描当前类的包及它的子包。
@EnableAutoConfiguration

这个也是个组合注解,由下面两个注解组成。
@AutoConfigurationPackage

@Import用于给容器注入某个组件类,上面注入了Registrar类。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// registerBeanDefinitions顾名思义是给Beandefinitions加东西
// packageName就是标注了@AutoConfigurationPackage的包名
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
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();
// BasePackages的源码可以看到,它有个List<String> packages
// 用来存储包名
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
由此可见,这个注解的作用就是将标注了@AutoConfigurationPackage的包名加入到容器已有的Bean中,Bean的名字为AutoConfigurationPackages.class.getName(),而Bean的class实际为BasePackages。
那么,将这些报名加入有何用呢?当然是为了之后从这个Bean里面取出来对应的类咯。取出来干吗用,就看具体场景了。
@Import(AutoConfigurationImportSelector.class)
将AutoConfigurationImportSelector注入到容器之中。
AutoConfigurationImportSelector可以将spring.factories下的KEY为AutoConfiguration的类"预注入"到容器,具体能不能注入要看它们的条件注入注解,springboot还提供了一个文件,用于提前判断是不是要"预注入,spirng-autoconfigure-metadata.properties,这个能优化springboot的启动速度,意味着不需要再由@Condition之类的注解判断是否能注入到容器,而是提前判断了。因为AutoConfiguration类都是注解了@Configuration的,里面的具体编码肯定是各种配置,由此就实现了自动装配的目的。
AutoConfigurationImportSelector实现自动装配原理就是它实现了DeferredImportSelector接口,重写了selectImports,下面就分析实现自动装配的源码。

loadMetadata

loadMetadata的作用就是我前面说的spirng-autoconfigure-metadata.properties优化机制。
getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 这个方法内有调用List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
// getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
// 第一个参数返回值是EnableAutoConfiguration.class 第二个参数是类加载器
// loadFactoryNames就是用这个加载器去加载spring.factories并返回Key为EnableAutoConfiguration的全限定类名的值,该值是一个列表,包含了各种自动配置类的全限定名
List<String> configurations =
getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
// 这个就是将我说的spirng-autoconfigure-metadata.properties不符合条件的获取到
// 后续就不用将它对应类传递给spring容器了,所以叫exclusions
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions
// Entry包含需要注入和不需要注入的,外部调用的方法只取了它的configurations部分
return new AutoConfigurationEntry(configurations, exclusions);
}
最后,返回了configurations给spring之后,spring将会根据对应的类名去进行注入,由此自动配置功能就得以实现了。

本文主要分析Spring Boot的@SpringBootApplication注解。它由@SpringBootConfiguration、@ComponentScan和@EnableAutoConfiguration组成。其中,@SpringBootConfiguration标识配置类,@ComponentScan指定扫描路径,@EnableAutoConfiguration实现自动装配,通过loadMetadata优化启动速度,最终实现自动配置功能。
1万+

被折叠的 条评论
为什么被折叠?



