Spring学习笔记-Spring boot中@SpringBootApplication 注解初探
特殊说明:代码块中的"…" 旨在屏蔽一些不太重要的代码
- 在SpringBoot的启动上加上@SpringBootApplication注解就可以简捷的创建一个Spring boot 应用程序,接下来对该注解进行一个初步的探索,以了解其背后的原理。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- SpringBootApplication是一个组合注解,主要包括了@SpringBootConfiguration、@ComponentScan、@EnableAutoConfiguration三个注解。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......
}
- 首先来看@SpringBootConfiguration,该注解主要包含了一个@Configuration注解,@Configuration注解主要被一个@Component注解标注。其中@Configuration在学习Spring的时候了解到该被该注解标注说明该类为一个配置类,由此可以得出@SpringBootConfiguration注解主要用来告诉Spring 容器被其标注的类为java配置类。
@Configuration
public @interface SpringBootConfiguration {
}
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
- 其次再来看@EnableAutoConfiguration,该注解主要包含@AutoConfigurationPackage、@Import({AutoConfigurationImportSelector.class})两个注解,其中@AutoConfigurationPackage注解主要包含了一个@Import({Registrar.class}),即@EnableAutoConfiguration注解,主要是通过@import导入了Registrar.class和AutoConfigurationImportSelector.class两个java配置类。
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
......
}
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
- Registrar.class主要通过实现ImportBeanDefinitionRegistrar, DeterminableImports两个接口,重写 registerBeanDefinitions方法将注解所在包及其子包下的所有组件注册进容器中 。由此可以得出为什么需要把被@SpringBootApplication标注的启动类放在其他类的父包或同一包中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
......
拿到注解的全信息,注解所在包及其子包下的组件
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
......
}
- AutoConfigurationImportSelector.class 主要通过selectImports方法中调用的getAutoConfigurationEntry方法进而调用getCandidateConfigurations方法 来获取spring-boot-autoconfigure-2.1.0.RELEASE.jar包下"META-INF/spring.factories"中的自动配置实体类。所有的自动配置类都在该properties文件中,但是这里的配置类并不会全部被导入,很多配置类上有@ConditionalOnClass这样的一个注解。只有在符合@ConditionalOnClass注解条件下的配置类才会被加载,这就是为什么我们在pom.xml只导入一个spring-boot-starter-web就会自动装配web的相关配置,省去了以前手动配置xml的步骤。
// 选择导入的配置类
public String[] selectImports(AnnotationMetadata annotationMetadata) {
......
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
......
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
......
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
......
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
......
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
- 最后的@ComponentScan注解,就是扫描当前配置类及其当前类所在包和子包下的组件