大家好,我是Java大蜗牛,一个渴望在互联网行业做到很牛的蜗牛。
可柔可刚,点赞则柔,白嫖则刚!死鬼~~~看完记得给我来个三连哦!
剖析@SpringBootApplication注解
创建一个SpringBoot工程后,SpringBoot会为用户提供一个Application类,该类负责项目的启动:
@SpringBootApplication
publicclassSpringbootSeniorApplication{
publicstaticvoidmain(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@interfaceSpringBootApplication {
}
抛却元注解不谈,@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@interfaceSpringBootConfiguration {
...}
在说明中提到, @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@interfaceComponentScan {
...}
在说明中我们可以得知: @ComponentScan 只负责指定要扫描的包,并没有装配其中的类,这个真正装配这些类是 @EnableAutoConfiguration 。
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration {
...}
该类真正完成了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@interfaceEnableAutoConfiguration {
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@interfaceAutoConfigurationPackage {
...}
从类的说明中我的得知,该注解用于导入并装配用户自定义类,即自动扫描包中的类。若该注解未通过 basePackages 或 basePackageClasses 参数指明要扫描的包路径,则默认扫描含该注解的类所在包及其子包。
@Import
用于导入并装配框架本身的类。其参数 AutoConfigurationImportSelector.java 类,该类用于导入自动配置的类。其装配跟踪入口: #getCandidateConfigurations
publicclassAutoConfigurationImportSelectorimplements
DeferredImportSelector,BeanClassLoaderAware,
ResourceLoaderAware,BeanFactoryAware,
EnvironmentAware,Ordered {
...
protected ListgetCandidateConfigurations(
AnnotationMetadata metadata, AnnotationAttributes attributes) { List 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.");
returnconfigurations;
} ...}
#getCandidateConfigurations -> SpringFactoriesLoader.loadFactoryNames
publicfinalclassSpringFactoriesLoader{
...publicstaticfinalString FACTORIES_RESOURCE_LOCATION ="META-INF/spring.factories";
...publicstaticList loadFactoryNames(ClassfactoryType, @NullableClassLoaderclassLoader){
String factoryTypeName = factoryType.getName();returnloadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}privatestaticMap> loadSpringFactories(@Nullable ClassLoader classLoader) {
...try{
Enumeration 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 -->
org.springframework.boot
spring-boot-starter-web
打开一个starter,如 spring-boot-starter-web 依赖,我们可以看到其中包含了一个子依赖:
<!-- spring-boot-starter-web-2.3.4.RELEASE.pom -->
...
org.springframework.boot
spring-boot-starter
2.3.4.RELEASE
compile
...
打开 spring-boot-starter 依赖,可以看到这么一个子依赖:
<!-- spring-boot-starter-2.3.4.RELEASE.pom -->
...
org.springframework.boot
spring-boot-autoconfigure
2.3.4.RELEASE
compile
...
查看该依赖的内容,打开spring.factories文件:
这些就是框架定义的,需要装配的类。
application.yml的加载
application.yml 文件对于 Spring Boot 来说是核心配置文件,至关重要!那么,该文件是如何加载到内存的呢?我们需要从启动类的 run() 方法开始跟踪,该跟踪过程比较深,耐心差的读者慎入。
@SpringBootApplication
publicclassSpringbootSeniorApplication{
publicstaticvoidmain(String[] args){
SpringApplication.run(SpringbootSeniorApplication.class,args);
}}
进入run方法:
publicclassSpringApplication{
...publicstaticConfigurableApplicationContext run(ClassprimarySource,String...args){
returnrun(newClass[]{ primarySource }, args);
}publicstaticConfigurableApplicationContext run(Class[]primarySources,String[]args){
returnnewSpringApplication(primarySources).run(args);
}publicConfigurableApplicationContext run(String... args) {
...// 准备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
...
}
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
...
// 让监听器监听环境准备过程
listeners.environmentPrepared(environment);
...
}
...
}
让监听器监听环境准备过程
classSpringApplicationRunListeners{
...voidenvironmentPrepared(ConfigurableEnvironment environment){
for(SpringApplicationRunListener listener :this.listeners) {
listener.environmentPrepared(environment); } } ...}
发布环境准备事件
publicclassEventPublishingRunListenerimplementsSpringApplicationRunListener,Ordered{
... @Override
publicvoidenvironmentPrepared(ConfigurableEnvironment environment){
this.initialMulticaster.multicastEvent(
newApplicationEnvironmentPreparedEvent(
this.application,
this.args,
environment ) ); } @Override
publicvoidmulticastEvent(ApplicationEventevent){
multicastEvent(event, resolveDefaultEventType(event));
} @Override
publicvoidmulticastEvent(final ApplicationEventevent, @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);
}
}
}
...
}
触发监听器
publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster{
...protectedvoidinvokeListener(ApplicationListener listener, ApplicationEventevent){
ErrorHandler errorHandler = getErrorHandler();if(errorHandler !=null) {
try{
doInvokeListener(listener,event);
}catch(Throwable err) {
errorHandler.handleError(err); } }else{
doInvokeListener(listener,event);
} }privatevoiddoInvokeListener(ApplicationListener listener, ApplicationEventevent){
... listener.onApplicationEvent(event);
... } ...}
ApplicationListener#onApplicationEvent 是一个接口方法,我们主要看它的 ConfigFileApplicationListener 实现类的实现
publicclassConfigFileApplicationListenerimplements...{
...@Override
publicvoidonApplicationEvent(ApplicationEvent event){
if(eventinstanceofApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } ... }privatevoidonApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event){
List 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 { voidload() {
FilteredPropertySource.apply( this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> { ...while(!this.profiles.isEmpty()) {
...load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast,false));
... } ... }); } private voidload(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> { boolean isDirectory = location.endsWith("/");
Set names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES; names.forEach((name) ->load(location, name, profile, filterFactory, consumer));
}); } private voidload(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 voidload(
PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) { ... List documents = loadDocuments(loader, name, resource); ... } private List loadDocuments( PropertySourceLoader loader, String name, Resource resource) throws IOException { DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource); List documents = this.loadDocumentsCache.get(cacheKey);if(documents == null) {
List>loaded= loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents); }returndocuments;
} } ...}
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;
}
}