SpringBoot源码系列(2):SpringBootApplication注解 前言 在SpringBoot项目中的主类上标注@SpringBootApplication
注解,便可实现基本的自动配置功能。本文通过源码了解其背后的原理。本文中SpringBoot版本号为2.7.5。
原文地址:https://xuedongyun.cn/post/50220/
@SpringBootApplication的构成 @SpringBootApplication
注解是一个复合注解,核心由@SpringBootConfiguration
, @EnableAutoConfiguration
, @ComponentScan
三部分构成。接下来我们分别来看三个核心注解的作用
1 2 3 4 5 6 7 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
@SpringBootConfiguration Main类本身也是SpringBoot中的配置类,只不过是核心配置类。@SpringBootConfiguration
核心其实就是@Configuration
1 2 3 @Configuration public @interface SpringBootConfiguration {}
@ComponentScan 用于指定扫描的类,Spring中的注解,可以具体看看。此处意义不大,暂且略过
@EnableAutoConfiguration 本次最核心的注解,用于开启自动配置。它也是一个复合注解,包含@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
两个部分。我们接下来详细看两个部分的代码。
1 2 3 4 @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage 翻译:自动配置包。我们查看其源码,发现它使用@Import
注解为容器导入了Registrar
组件。
1 2 3 4 @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
AutoConfigurationPackages.Registrar
组件为容器导入了一系列组件
我们这里用到了@Import
的高级用法
ImportBeanDefinitionRegistrar
接口:允许自动配置类向Spring IoC容器中注册额外的组件
通过@Import
注解导入类时,若该类实现了此接口,会调用其registerBeanDefinitions
方法 DeterminableImports
接口:让Spring Boot在自动配置过程中确定哪些类应该被添加到自动配置类的导入列表中
当SpringBoot需要自动配置某个类时,若该类实现了此接口,会调用其determineImports
方法,用于确定哪些类应该被添加到自动配置类的导入列表中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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)); } }
现在,我们Main类所在包下的所有组件,都被自动导入了
@Import(AutoConfigurationImportSelector.class) 通过@Import注解导入了AutoConfigurationImportSelector
类型的Bean。AutoConfigurationImportSelector
实现了ImportSelector
接口,被用于动态地确定应该导入哪些自动配置类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
继续查看getAutoConfigurationEntry
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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); }
getCandidateConfigurations
方法获取了所有的候选配置,通过调试可以大概看到
1 2 3 4 5 "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration" "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration" "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration" "org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration" ...
继续查看getCandidateConfigurations
方法。
会根据传入的AnnotationMetadata
和AnnotationAttributes
参数,使用SpringFactoriesLoader
工具类,加载所有在META-INF/spring.factories
文件中声明的类,并将它们添加到一个List
中。 还通过ImportCandidates.load()
方法,加载了所有在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中声明的AutoConfiguration
类,并将它们也添加到了上述的List
中。(最新的标准,推荐使用它) 我们简单看看SpringFactoriesLoader.loadFactoryNames
的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 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, "..." ); return configurations; }
1 2 3 4 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static Map<String, List<String>> loadSpringFactories (ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource (url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { } } return result; }
举例说明:spring-boot-test-autoconfigure
的META-INF/spring.factories
文件为以下内容,其中内容为:接口名+实现类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # DefaultTestExecutionListenersPostProcessors org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor=\ org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener$PostProcessor # Spring Test ContextCustomizerFactories org.springframework.test.context.ContextCustomizerFactory=\ org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory # Test Execution Listeners org.springframework.test.context.TestExecutionListener=\ org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\ org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\ org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\ org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener,\ org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener
2.7.0以后,不推荐将配置写入spring.factories
中了,推荐写在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
里
自动配置类会按需开启 虽然上述场景中,所有自动配置启动时默认加载全部(xxxAutoConfiguration
),但是按照每个类的装配规则(@Conditional
),最终还是会按需配置
例如:AopAutoConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @AutoConfiguration @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { } }