分析 @SpringBootApplication 注解
创立一个 SpringBoot 工程后,SpringBoot 会为用户提供一个 Application 类,该类负责我的项目的启动:
@SpringBootApplication
public class SpringbootSeniorApplication {public static void main(String[] args) {SpringApplication.run(SpringbootSeniorApplication.class, args);
}
}
这是一个被 @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 { }
抛却元注解不谈,@SpringBootApplication 继承了三个注解:
@SpringBootConfiguration
/**
* Indicates that a class provides Spring Boot application
* {@link Configuration @Configuration}. Can be used as an
* alternative to the Spring's standard @Configuration
* annotation so that configuration can be found
* automatically (for example in tests).
*
* Application should only ever include one
* @SpringBootConfiguration and most idiomatic Spring Boot
* applications will inherit it from @SpringBootApplication.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {...}
在阐明中提到,@SpringBootConfiguration
注解是用来代替 Spring 的@Configuration
,不便 SpringBoot 主动找到配置。
@ComponentScan
/**
* Configures component scanning directives
* for use with Configuration classes.
* Provides support parallel with Spring XML's
* <context:component-scan> element.
*
* Either #basePackageClasses or #basePackages
* (or its alias #value} may be specified to
* define specific packages to scan. If specific
* packages are not defined, scanning will occur
* from the package of the class that declares
* this annotation.
*
* Note that the <context:component-scan> element
* has an annotation-config attribute; however,
* this annotation does not. This is because
* in almost all cases when using @ComponentScan,
* default annotation config processing
* (e.g. processing @Autowired and friends)
* is assumed. Furthermore, when using
* AnnotationConfigApplicationContext,
* annotation config processors are always
* registered, meaning that any attempt to disable
* them at the @ComponentScan level would be ignored.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {...}
在阐明中咱们能够得悉:@ComponentScan
只负责指定要扫描的包,并没有拆卸其中的类,这个真正拆卸这些类是@EnableAutoConfiguration
。
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
该类真正实现了 SpringBoot 对于类的拆卸工作,具体内容在后续会作出解释。
以 @Enable 结尾的注解
以 @Enable 结尾的注解 (@EnableXxx
) 个别用于开启某一项性能,是为了简化代码的导入。它是一个组合注解,个别状况下 @EnableXxx
注解中都会组合一个 @Import
注解,而该 @Import
注解用于导入指定的类,而被导入的类个别有三种:
配置类
- 类的特色:@Import 中指定的类个别以 Configuration 结尾
- 类的配置:该类上会注解 @Configuration
-
类的案例:定时工作启动注解:
SchedulingConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { }
选择器
- 类的特色:@Import 中指定的类个别以 Selector 结尾
- 类的配置:该类间接或间接实现了
ImportSelector
接口,示意以后类会依据条件抉择导入不同的类。 -
类的案例:Redis 配置类:
CachingConfigurationSelector
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(CachingConfigurationSelector.class) public @interface EnableCaching {...}
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> { ... @Override public String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) { case PROXY: return getProxyImports(); case ASPECTJ: return getAspectJImports(); default: return null; } } ... }
注册器
- 类的特色:@Import 中指定的类个别以 Registrar 结尾。
- 类的配置:该类间接或间接实现了
ImportBeanDefinitionRegistrar
接口,用于导入注册器,该类能够在代码运行时动静注册指定类的实例。 -
类的案例:AspectJ:
AspectJAutoProxyRegistrar
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy {...}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
解析 @EnableAutoConfiguration
@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 {};}
该注解是一个组合注解,用于实现主动配置,它是 Spring Boot 的外围注解。所谓主动配置是指,将用户自定义的类及框架自身用到的类进行拆卸。
@AutoConfigurationPackage
/**
* Registers packages with AutoConfigurationPackages.
* When no #basePackages base packages or
* #basePackageClasses base package classes are
* specified, the package of the annotated class is
* registered.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}
从类的阐明中我的得悉,该注解用于导入并拆卸用户自定义类,即主动扫描包中的类。若该注解未通过 basePackages
或basePackageClasses
参数指明要扫描的包门路,则默认扫描含该注解的类所在包及其子包。
@Import
用于导入并拆卸框架自身的类。其参数 AutoConfigurationImportSelector.java
类,该类用于导入主动配置的类。其拆卸跟踪入口:#getCandidateConfigurations
public class AutoConfigurationImportSelector implements
DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware,
EnvironmentAware, Ordered {
...
protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
...
}
#getCandidateConfigurations
-> SpringFactoriesLoader.loadFactoryNames
public final class SpringFactoriesLoader {
...
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
...
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
...
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
...
} catch (IOException ex) {...}
}
}
追踪到这里,咱们得悉,框架自身定义的类是从 META-INF/spring.factories
文件中获取的。该文件目录在哪儿呢?
在创立 SpringBoot Web 我的项目时,咱们在 pom.xml 文件中会主动导入一个依赖:
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
关上一个 starter,如 spring-boot-starter-web
依赖,咱们能够看到其中蕴含了一个子依赖:
<!-- spring-boot-starter-web-2.3.4.RELEASE.pom -->
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
...
</dependencies>
关上 spring-boot-starter
依赖,能够看到这么一个子依赖:
<!-- spring-boot-starter-2.3.4.RELEASE.pom -->
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
...
</dependencies>
查看该依赖的内容,关上 spring.factories 文件:
这些就是框架定义的,须要拆卸的类。
application.yml 的加载
application.yml
文件对于 Spring Boot 来说是外围配置文件,至关重要!那么,该文件是如何加载到内存的呢?咱们须要从启动类的 run()
办法开始跟踪,该跟踪过程比拟深,急躁差的读者慎入。
@SpringBootApplication
public class SpringbootSeniorApplication {public static void main(String[] args) {SpringApplication.run(SpringbootSeniorApplication.class, args);
}
}
进入 run 办法:
public class SpringApplication {
...
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] {primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
...
// 筹备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
...
}
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
...
// 让监听器监听环境筹备过程
listeners.environmentPrepared(environment);
...
}
...
}
让监听器监听环境筹备过程
class SpringApplicationRunListeners {
...
void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);
}
}
...
}
公布环境筹备事件
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
...
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(
new ApplicationEnvironmentPreparedEvent(
this.application,
this.args,
environment
)
);
}
@Override
public void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {
// 触发监听器
executor.execute(() -> invokeListener(listener, event));
}
else {invokeListener(listener, event);
}
}
}
...
}
触发监听器
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
...
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {doInvokeListener(listener, event);
}
catch (Throwable err) {errorHandler.handleError(err);
}
}
else {doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
...
listener.onApplicationEvent(event);
...
}
...
}
ApplicationListener#onApplicationEvent
是一个接口办法,咱们次要看它的 ConfigFileApplicationListener
实现类的实现
public class ConfigFileApplicationListener implements ... {
...
@Override
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
...
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
...
}
EnvironmentPostProcessor#postProcessEnvironment
是一个接口办法,咱们次要看它的 ConfigFileApplicationListener
实现类的实现
public class ConfigFileApplicationListener implements ... {
...
@Override
public void postProcessEnvironment(
ConfigurableEnvironment environment,
SpringApplication application) {
// 加载配置文件
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(
ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();}
private class Loader {void load() {
FilteredPropertySource.apply(
this.environment,
DEFAULT_PROPERTIES,
LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
...
while (!this.profiles.isEmpty()) {
...
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
...
}
...
});
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {getSearchLocations().forEach((location) -> {boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
...
for (PropertySourceLoader loader : this.propertySourceLoaders) {for (String fileExtension : loader.getFileExtensions()) {if (processed.add(fileExtension)) {loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer);
}
}
}
}
private void loadForFileExtension(
PropertySourceLoader loader,
String prefix,
String fileExtension,
Profile profile,
DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
...
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(
PropertySourceLoader loader,
String location,
Profile profile,
DocumentFilter filter,
DocumentConsumer consumer) {
...
List<Document> documents = loadDocuments(loader, name, resource);
...
}
private List<Document> loadDocuments(
PropertySourceLoader loader,
String name,
Resource resource) throws IOException {DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {List<PropertySource<?>> loaded = loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
}
...
}
PropertySourceLoader#getFileExtensions
和 PropertySourceLoader#load
都是接口办法,咱们次要看它的 YamlPropertySourceLoader
实现类的实现
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {return new String[] {"yml", "yaml"};
}
@Override
public List<PropertySource<?>> load(
String name,
Resource resource) throws IOException {
...
return propertySources;
}
}
最初
感激你看到这里,文章有什么有余还请斧正,感觉文章对你有帮忙的话记得给我点个赞!