Spring Cloud源码研读(一):启动与Bean加载

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章