SpringBoot启动核心逻辑
通常最简单的springboot项目的总入口是如下写法。类上加注解@SpringBootApplication,然后直接调用静态方法SpringApplication#run(Class<?> primarySource,String… args):
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
注解@SpringBootApplication定义了一系列SpringBoot提供的自动化配置功能。@SpringBootApplication是个组合注解,核心内容如下:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
}
实际springboot的核心启动逻辑在SpringApplication#run(String… args)实例方法中。这个代码块主要实现了以下功能:
①、通过SpringFactoriesLoader查找并加载所有的 SpringApplicationRunListeners,通过调用starting()方法通知所有的SpringApplicationRunListeners应用开始启动了。
②、创建并配置当前应用将要使用的 Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties)。
③、SpringBoot语法糖,应用在启动时输出Banner。
④、根据是否是web项目,来创建不同的ApplicationContext容器。
⑤、创建一系列 FailureAnalyzer。
⑥、初始化ApplicationContext。
⑦、调用ApplicationContext的 refresh()方法,完成IoC容器可用的最后一道工序。
⑧、查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。
⑨、执行所有SpringApplicationRunListener的finished()方法。
SpringBoot 创建ApplicationContext容器
①、在上述springboot启动过程中,SpringApplicationRunListeners加载了一个EventPublishingRunListener对象实例。
②、在创建Environment后,EventPublishingRunListener发布了一个ApplicationEnvironmentPreparedEvent事件,BootstrapApplicationListener#onApplicationEvent()订阅了该事件。
③、BootstrapApplicationListener#onApplicationEvent()中,创建了一个新的SpringApplication和一个AnnotationConfigApplicationContext。并执行SpringApplication#run()。
④、SpringApplication#run()中启动bean的扫描注册和容器上下文的加载。
ApplicationContext初始化
上述第④简化后的调用堆栈如下:
1. SpringApplication#run()
2. AbstractApplicationContext#refresh()
3. AbstractApplicationContext#invokeBeanFactoryPostProcessors()
4. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
5. ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry)
6. ConfigurationClassParser#parse(candidates)
7. ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass,sourceClass)
第2步AbstractApplicationContext#refresh()执行beanFactory完成初始化的完整步骤,直接复制代码如下:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//注册需要加入beanFactory的所有bean
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
//执行单例bean的实例化
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
第5步在loadBeanDefinitions时,执行了ImportBeanDefinitionRegistrar,实现Bean的动态加载。完成了processConfigBeanDefinitions执行完成后,spring获得了所有需要加载的bean候选集合Set。
5.1. ConfigurationClassPostProcessor.processConfigBeanDefinitions()
5.2. ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()
5.3. ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars()
5.4. ImportBeanDefinitionRegistrar.registerBeanDefinitions()
第6步ConfigurationClassParser会对每一个bean递归查找内部的Bean声明。
第7步ConfigurationClassParser#doProcessConfigurationClass()是spring bean注册扫描核心类。在源代码中作者给出了十分良心的注释,直接复制粘贴如下:
7.1. // Process any @PropertySource annotations
7.2. // Process any @ComponentScan annotations
7.3. // Process any @Import annotations
7.4. // Process any @ImportResource annotations
7.5. // Process individual @Bean methods
此处@ComponentScan扫描的Bean包路径就是基于@SpringBootApplication注解声明的路径。ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()。
7.3.1. ConfigurationClassParser.doProcessConfigurationClass(C)
7.3.2. ConfigurationClassParser.getImports(SourceClass)
7.3.3. ConfigurationClassParser.collectImports(SourceClass, Set<SourceClass>, Set<SourceClass>)
7.3.4. ConfigurationClassParser$SourceClass.getAnnotationAttributes(String, String)
第7.3.2步getImports()是springboot启动非常关键的一步,通过sourceClass “MyApplication"的驱动,递归找到了AutoConfigurationImportSelector,从而实现了springboot的自动加载过程。
// 注解链
@SpringBootApplication
=> @EnableAutoConfiguration
=> @Import(AutoConfigurationImportSelector.class)
7.3导入@Import注解value时,分了三个类型处理:
-
@Configuration class:普通的配置bean,导入其内部bean声明
-
ImportSelector:基于条件选择加载不同的@Configuration class
-
ImportBeanDefinitionRegistrar:一般是工厂模式实现动态加载Bean
springboot自动配置过程总结
- @EnableAutoConfiguration注解表示开启Spring Boot自动配置功能,Spring Boot会根据应用的依赖、自定义的bean、classpath下有没有某个类 等等因素来猜测你需要的bean,然后注册到IOC容器中。
- @Import注解用于导入类,并将这个类作为一个bean的定义注册到容器中,这里它将把 EnableAutoConfigurationImportSelector作为bean注入到容器中,而这个类会将所有符合条件的@Configuration配置都加载到容器中
案例点睛
@EnableJpaRepositories
在Spring Boot中,我们会经常遇到@EnableXXX用来激活我们某一个功能性的模块,通过类注解激活后我们就能使用所激活的配置给我们带来的功能。通过SpringBoot启动过程的分析我们知道,@EnableXXX实际上是通过引入@Import注解从而进一步引入具体的配置文件。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {
}
autoconfigure
在实际开发中,我们发现并没有使用@EnableJpaRepositories,但也可以直接使用JPA相关的功能。这是由于AutoConfigurationImportSelector通过SpringFactoriesLoader动态加载了JpaRepositoriesAutoConfigureRegistrar,从而实现引入@EnableJpaRepositories。
# spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfigureRegistrar,\
引用传递:
@EnableAutoConfiguration
=> JpaRepositoriesAutoConfiguration
=> @Import(JpaRepositoriesAutoConfigureRegistrar.class)
=> @EnableJpaRepositories
=> JpaRepositoryFactoryBean
Bean的动态注入机制
在使用JPA时,我们只声明了MyEntityJpaRepository的接口,没有任何具体的实现类,但是在服务层我们可以注入该接口类型的bean并使用。这是由于JpaRepositoriesAutoConfigureRegistrar实现了ImportBeanDefinitionRegistrar,从而将所有包扫描路径下的Repository接口都注册为bean,并通过JpaRepositoryFactoryBean动态生成具体的代理实现。
Spring bean实例化时间点
第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化。
第二:如果你使用ApplicationContext作为Spring Bean的工厂类,则又分为以下几种情况:
(1):如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则 ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候, 直接从这个缓存中取
(2):如果bean的scope是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
(3):如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
第一种场景:前述流程”ApplicationContext初始化“第4步PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()。
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
第二种场景:前述流程”ApplicationContext初始化“第2步AbstractApplicationContext#refresh()中的finishBeanFactoryInitialization(beanFactory)。
Spring Boot 手动配置@Enable的秘密: http://www.jerome.xin/articles/spring-boot-enable-congfiure
Spring Data JPA 工作原理 : 自定义JpaRespository接口却不用提供实现:https://blog.csdn.net/andy_zhang2007/article/details/84064862
SpringBoot启动流程解析:https://www.cnblogs.com/trgl/p/7353782.html