网站建设最新模板下载,企业设计网站推荐,wordpress 应用商店模板,域名可以绑定网站吗前言
主要了解前面的程序入口 SpringBootApplication 这个注解的结构。
正文
参考《SpringBoot揭秘 快速构建微服务体系》第三章的学习#xff0c;总结下。
SpringBootApplication背后的秘密
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
In…前言
主要了解前面的程序入口 SpringBootApplication 这个注解的结构。
正文
参考《SpringBoot揭秘 快速构建微服务体系》第三章的学习总结下。
SpringBootApplication背后的秘密
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 {
...
}虽然定义使用了多个Annotation进行了原信息标注但实际上重要的只有三个Annotation
ConfigurationSpringBootConfiguration点开查看发现里面还是应用了ConfigurationEnableAutoConfigurationComponentScan
所以如果我们使用如下的SpringBoot启动类整个SpringBoot应用依然可以与之前的启动类功能对等
Configuration
EnableAutoConfiguration
ComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}但每次都写三个Annotation显然过于繁琐所以写一个SpringBoot-Application这样的一站式复合Annotation显然更方便些。
Configuration创世纪 这里的Configuration对我们来说并不陌生它就是JavaConfig形式的Spring IoC容器的配置类使用的那个Configuration既然SpringBoot应用骨子里就是一个Spring应用那么自然也需要加载某个IoC容器的配置而SpringBoot社区推荐使用基于JavaConfig的配置形式所以很明显这里的启动类标注了Configuration之后本身其实也是一个IoC容器的配置类 很多SpringBoot的代码示例都喜欢在启动类上直接标注Configuration或者SpringBootApplication对于初接触SpringBoot的开发者来说其实这种做法不便于理解如果我们将上面的SpringBoot启动类拆分为两个独立的Java类整个形势就明朗了 Configuration
EnableAutoConfiguration
ComponentScan
public class DemoConfiguration {Beanpublic Controller controller() {return new Controller();}
}
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoConfiguration.class, args);}
}所以启动类DemoApplication其实就是一个标准的Standalone类型Java程序的main函数启动类没有什么特殊的。 而Configuration标注的DemoConfiguration定义其实也是一个普通的JavaConfig形式的IoC容器配置类没啥新东西全是Spring框架里的概念 不要被这个长篇大论弄模糊了这个其实在以前学习Spring中也有这些注解Spring容器中为了简化XMl配置允许使用JavaConfig注册一个Bean。就是使用的是Configuration每个拥有注解Bean的函数的返回值都将会在SPring启动时候注册到容器中可以使用自动装配如下一个JavaConfig的注册Bean
Configuration
public class Configs {Value(classpath:data.json)protected File configFile;Beanpublic PersonCfg readServerConfig() throws IOException {return new ObjectMapper().readValue(configFile, PersonCfg.class);}EnableAutoConfiguration的功效 EnableAutoConfiguration其实也没啥“创意”各位是否还记得Spring框架提供的各种名字为Enable开头的Annotation定义比如EnableScheduling、EnableCaching、EnableMBeanExport等EnableAutoConfiguration的理念和“做事方式”其实一脉相承简单概括一下就是借助Import的支持收集和注册特定场景相关的bean定义 * Enable Scheduling是通过Import将Spring调度框架相关的bean定义都加载到IoC容器。 * Enable M Bean Export是通过Import将JMX相关的bean定义加载到IoC容器。 而EnableAutoConfiguration也是借助Import的帮助将所有符合自动配置条件的bean定义加载到IoC容器仅此而已 EnableAutoConfiguration作为一个复合Annotation其自身定义关键信息如下 Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Inherited
AutoConfigurationPackage
Import(EnableAutoConfigurationImportSelector.class)
public interface EnableAutoConfiguration {...
}其中最关键的要属Import(EnableAutoConfigurationImportSelector.class)借 助EnableAutoConfigurationImportSelector, EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的Configuration配置都加载到当前SpringBoot创建并使用的IoC容器就跟一只“八爪鱼”一样。 借助于Spring框架原有的一个工具类SpringFactoriesLoader的支持EnableAutoConfiguration可以“智能”地自动配置功效才得以大功告成 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zrSW4kJm-1637076750895)(https://ws1.sinaimg.cn/large/ece1c17dgy1fnoe3ayl8oj20qd0pf0vs.jpg)]
自动配置幕后英雄SpringFactoriesLoader详解
SpringFactoriesLoader属于Spring框架私有的一种扩展方案其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。
public abstract class SpringFactoriesLoader {//...public static T ListT loadFactories(ClassT factoryClass, ClassLoader classLoader) {...}public static ListString loadFactoryNames(Class? factoryClass, ClassLoader classLoader) {....}
}配合EnableAutoConfiguration使用的话它更多是提供一种配置查找的功能支持即根据EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组Configuration类
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader# Run Listeners
org.springframework.boot.SpringApplicationRunListener\
org.springframework.boot.context.event.EventPublishingRunListener# Application Context Initializers
org.springframework.context.ApplicationContextInitializer\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer# Application Listeners
org.springframework.context.ApplicationListener\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter以上是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容可以很好地说明问题。 以EnableAutoConfiguration自动配置的魔法其实就变成了从classpath中搜寻所有META-INF/spring.factories配置文件并将其中org.spring-framework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射Java Reflection实例化为对应的标注了Configuration的JavaConfig形式的IoC容器配置类然后汇总为一个并加载到IoC容器。 可有可无的Configuration Component Scan的功能其实就是自动扫描并加载符合条件的组件或bean定义最终将这些bean定义加载到容器中。加载bean定义到Spring的IoC容器我们可以手工单个注册不一定非要通过批量的自动扫描完成所以说Component Scan是可有可无的。 深入探索SpringApplication执行流程
SpringApplication的run方法的实现是我们本次旅程的主要线路 该方法的主要流程大体可以归纳如下
如果我们使用的是SpringApplication的静态run方法那么这个方法里面首先需要创建一个SpringApplication对象实例然后调用这个创建好的SpringApplication的实例run方法。在SpringApplication实例初始化的时候它会提前做几件事情 根据classpath里面是否存在某个特征类org.springframework.web.context.ConfigurableWebApplicationContext来决定是否应该创建一个为Web应用使用的ApplicationContext类型还是应该创建一个标准Standalone应用使用的ApplicationContext类型。使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。推断并设置main方法的定义类。 SpringApplication实例初始化完成并且完成设置后就开始执行run方法的逻辑了方法执行伊始首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener调用它们的started()方法告诉这些SpringApplicationRunListener“嘿SpringBoot应用要开始执行咯”。创建并配置当前SpringBoot应用将要使用的Environment包括配置要使用的PropertySource以及Profile。遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法告诉它们“当前SpringBoot应用使用的Environment准备好咯”。如果SpringApplication的showBanner属性被设置为true则打印bannerSpringBoot 1.3.x版本这里应该是基于Banner.Mode决定banner的打印行为。这一步的逻辑其实可以不关心我认为唯一的用途就是“好玩”Just For Fun。根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成然后根据条件决定是否添加ShutdownHook决定是否使用自定义的BeanNameGenerator决定是否使用自定义的ResourceLoader当然最重要的将之前准备好的Environment设置给创建好的ApplicationContext使用。ApplicationContext创建好之后SpringApplication会再次借助Spring-FactoriesLoader查找并加载classpath中所有可用的ApplicationContext-Initializer然后遍历调用这些ApplicationContextInitializer的initialize (applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。遍历调用所有SpringApplicationRunListener的contextPrepared()方法 通知它们“SpringBoot应用使用的ApplicationContext准备好啦”最核心的一步将之前通过EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。遍历调用所有SpringApplicationRunListener的contextLoaded()方法告知所有SpringApplicationRunListenerApplicationContext”装填完毕”!调用ApplicationContext的refresh()方法完成IoC容器可用的最后一道工序。查找当前ApplicationContext中是否注册有CommandLineRunner如果有则遍历执行它们。正常情况下遍历执行SpringApplicationRunListener的finished()方法告知它们“搞定”。如果整个过程出现异常则依然调用所有SpringApplicationRunListener的finished()方法只不过这种情况下会将异常信息一并传入处理。
至此一个完整的SpringBoot应用启动完毕
整个过程看起来冗长无比但其实很多都是一些事件通知的扩展点如果我们将这些逻辑暂时忽略那么其实整个SpringBoot应用启动的逻辑就可以压缩到极其精简的几步。
参考文章
《SpringBoot揭秘 快速构建微服务体系》 第三章