7.Spring之啓動過程源碼解析

前言分析

通常,我們說的Spring啓動,就是構造ApplicationContext對象以及調用refresh()方法的過程。

首先,Spring啓動過程主要做了這麼幾件事情:

  1. 構造一個BeanFactory對象
  2. 解析配置類,得到BeanDefinition,並註冊到BeanFactory中
    1. 解析@ComponentScan,此時就會完成掃描
    2. 解析@Import
    3. 解析@Bean
    4. ...
  1. 因爲ApplicationContext還支持國際化,所以還需要初始化MessageSource對象
  2. 因爲ApplicationContext還支持事件機制,所以還需要初始化ApplicationEventMulticaster對象
  3. 把用戶定義的ApplicationListener對象添加到ApplicationContext中,等Spring啓動完了就要發佈事件了
  4. 創建非懶加載的單例Bean對象,並存在BeanFactory的單例池中。
  5. 調用Lifecycle Bean的start()方法
  6. 發佈ContextRefreshedEvent事件

由於Spring啓動過程中要創建非懶加載的單例Bean對象,那麼就需要用到BeanPostProcessor,所以Spring在啓動過程中就需要做兩件事:

  1. 生成默認的BeanPostProcessor對象,並添加到BeanFactory中
    1. AutowiredAnnotationBeanPostProcessor:處理@Autowired、@Value
    2. CommonAnnotationBeanPostProcessor:處理@Resource、@PostConstruct、@PreDestroy
    3. ApplicationContextAwareProcessor:處理ApplicationContextAware等回調
  1. 找到外部用戶所定義的BeanPostProcessor對象(類型爲BeanPostProcessor的Bean對象),並添加到BeanFactory中

BeanFactoryPostProcessor

BeanPostProcessor表示Bean的後置處理器,是用來對Bean進行加工的,類似的,BeanFactoryPostProcessor理解爲BeanFactory的後置處理器,用來用對BeanFactory進行加工的。

Spring支持用戶定義BeanFactoryPostProcessor的實現類Bean,來對BeanFactory進行加工,比如:

@Component
public class ZhouyuBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
		beanDefinition.setAutowireCandidate(false);
	}
}

以上代碼,就利用了BeanFactoryPostProcessor來拿到BeanFactory,然後獲取BeanFactory內的某個BeanDefinition對象並進行修改,注意這一步是發生在Spring啓動時,創建單例Bean之前的,所以此時對BeanDefinition就行修改是會生效的。

注意:在ApplicationContext內部有一個核心的DefaultListableBeanFactory,它實現了ConfigurableListableBeanFactory和BeanDefinitionRegistry接口,所以ApplicationContext和DefaultListableBeanFactory是可以註冊BeanDefinition的,但是ConfigurableListableBeanFactory是不能註冊BeanDefinition的,只能獲取BeanDefinition,然後做修改。

所以Spring還提供了一個BeanFactoryPostProcessor的子接口:BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

我們可以看到BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor接口,並新增了一個方法,注意方法的參數爲BeanDefinitionRegistry,所以如果我們提供一個類來實現BeanDefinitionRegistryPostProcessor,那麼在postProcessBeanDefinitionRegistry()方法中就可以註冊BeanDefinition了。比如:

@Component
public class ZhouyuBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition.setBeanClass(User.class);
		registry.registerBeanDefinition("user", beanDefinition);
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
		beanDefinition.setAutowireCandidate(false);
	}
}

如何理解refresh()?

/**
	 * Load or refresh the persistent representation of the configuration,
	 * which might an XML file, properties file, or relational database schema.
	 * <p>As this is a startup method, it should destroy already created singletons
	 * if it fails, to avoid dangling resources. In other words, after invocation
	 * of that method, either all or no singletons at all should be instantiated.
	 * @throws BeansException if the bean factory could not be initialized
	 * @throws IllegalStateException if already initialized and multiple refresh
	 * attempts are not supported
	 */
	void refresh() throws BeansException, IllegalStateException;

這是ConfigurableApplicationContext接口上refresh()方法的註釋,意思是:加載或刷新持久化的配置,可能是XML文件、屬性文件或關係數據庫中存儲的。由於這是一個啓動方法,如果失敗,它應該銷燬已經創建的單例,以避免暫用資源。換句話說,在調用該方法之後,應該實例化所有的單例,或者根本不實例化單例 。

有個理念需要注意:ApplicationContext關閉之後不代表JVM也關閉了,ApplicationContext是屬於JVM的,說白了ApplicationContext也是JVM中的一個對象。

在Spring的設計中,也提供可以刷新的ApplicationContext和不可以刷新的ApplicationContext。比如:

AbstractRefreshableApplicationContext extends AbstractApplicationContext

就是可以刷新的

GenericApplicationContext extends AbstractApplicationContext

就是不可以刷新的。

AnnotationConfigApplicationContext繼承的是GenericApplicationContext,所以它是不能刷新的。

AnnotationConfigWebApplicationContext繼承的是AbstractRefreshableWebApplicationContext,所以它是可以刷的。

上面說的不能刷新是指不能重複刷新,只能調用一次refresh方法,第二次時會報錯。

refresh()底層原理流程

底層原理流程圖:https://www.processon.com/view/link/5f60a7d71e08531edf26a919

下面以AnnotationConfigApplicationContext爲例子,來介紹refresh的底層原理。

  1. 在調用AnnotationConfigApplicationContext的構造方法之前,會調用父類GenericApplicationContext的無參構造方法,會構造一個BeanFactory,爲DefaultListableBeanFactory
  2. 構造AnnotatedBeanDefinitionReader(主要作用添加一些基礎的PostProcessor,同時可以通過reader進行BeanDefinition的註冊),同時對BeanFactory進行設置和添加PostProcessor(後置處理器)
    1. 設置dependencyComparator:AnnotationAwareOrderComparator,它是一個Comparator,是用來進行排序的,會獲取某個對象上的Order註解或者通過實現Ordered接口所定義的值進行排序,在日常開發中可以利用這個類來進行排序
    2. 設置autowireCandidateResolver:ContextAnnotationAutowireCandidateResolver,用來解析某個Bean能不能進行自動注入,比如某個Bean的autowireCandidate屬性是否等於true
    3. 向BeanFactory中添加ConfigurationClassPostProcessor對應的BeanDefinition,它是一個BeanDefinitionRegistryPostProcessor,並且實現了PriorityOrdered接口
    4. 向BeanFactory中添加AutowiredAnnotationBeanPostProcessor對應的BeanDefinition,它是一個InstantiationAwareBeanPostProcessorAdapter,MergedBeanDefinitionPostProcessor
    5. 向BeanFactory中添加CommonAnnotationBeanPostProcessor對應的BeanDefinition,它是一個InstantiationAwareBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor
    6. 向BeanFactory中添加EventListenerMethodProcessor對應的BeanDefinition,它是一個BeanFactoryPostProcessor,SmartInitializingSingleton
    7. 向BeanFactory中添加DefaultEventListenerFactory對應的BeanDefinition,它是一個EventListenerFactory
  1. 構造ClassPathBeanDefinitionScanner(主要作用可以用來掃描得到並註冊BeanDefinition),同時進行設置:
    1. 設置this.includeFilters = AnnotationTypeFilter(Component.class)
    2. 設置environment
    3. 設置resourceLoader
  1. 利用reader註冊AppConfig爲BeanDefinition,類型爲AnnotatedGenericBeanDefinition
  2. 接下來就是調用refresh方法
  3. prepareRefresh():
    1. 記錄啓動時間
    2. 可以允許子容器設置一些內容到Environment中
    3. 驗證Environment中是否包括了必須要有的屬性
  1. obtainFreshBeanFactory():進行BeanFactory的refresh,在這裏會去調用子類的refreshBeanFactory方法,具體子類是怎麼刷新的得看子類,然後再調用子類的getBeanFactory方法,重新得到一個BeanFactory
  2. prepareBeanFactory(beanFactory):
    1. 設置beanFactory的類加載器
    2. 設置表達式解析器:StandardBeanExpressionResolver,用來解析Spring中的表達式
    3. 添加PropertyEditorRegistrar:ResourceEditorRegistrar,PropertyEditor類型轉化器註冊器,用來註冊一些默認的PropertyEditor
    4. 添加一個Bean的後置處理器:ApplicationContextAwareProcessor,是一個BeanPostProcessor,用來執行EnvironmentAware、ApplicationEventPublisherAware等回調方法
    5. 添加ignoredDependencyInterface:可以向這個屬性中添加一些接口,如果某個類實現了這個接口,並且這個類中的某些set方法在接口中也存在,那麼這個set方法在自動注入的時候是不會執行的,比如EnvironmentAware這個接口,如果某個類實現了這個接口,那麼就必須實現它的setEnvironment方法,而這是一個set方法,和Spring中的autowire是衝突的,那麼Spring在自動注入時是不會調用setEnvironment方法的,而是等到回調Aware接口時再來調用(注意,這個功能僅限於xml的autowire,@Autowired註解是忽略這個屬性的)

a.EnvironmentAware

b.EmbeddedValueResolverAware

c.ResourceLoaderAware

d.ApplicationEventPublisherAware

e.MessageSourceAware

f.ApplicationContextAware

g.另外其實在構造BeanFactory的時候就已經提前添加了另外三個:

h.BeanNameAware

i.BeanClassLoaderAware

j.BeanFactoryAware

    1. 添加resolvableDependencies:在byType進行依賴注入時,會先從這個屬性中根據類型找bean

a.BeanFactory.class:當前BeanFactory對象

b.ResourceLoader.class:當前ApplicationContext對象

c.ApplicationEventPublisher.class:當前ApplicationContext對象

d.ApplicationContext.class:當前ApplicationContext對象

    1. 添加一個Bean的後置處理器:ApplicationListenerDetector,是一個BeanPostProcessor,用來判斷某個Bean是不是ApplicationListener,如果是則把這個Bean添加到ApplicationContext中去,注意一個ApplicationListener只能是單例的
    2. 添加一個Bean的後置處理器:LoadTimeWeaverAwareProcessor,是一個BeanPostProcessor,用來判斷某個Bean是不是實現了LoadTimeWeaverAware接口,如果實現了則把ApplicationContext中的loadTimeWeaver回調setLoadTimeWeaver方法設置給該Bean。
    3. 添加一些單例bean到單例池:

a."environment":Environment對象

b."systemProperties":System.getProperties()返回的Map對象

c."systemEnvironment":System.getenv()返回的Map對象

  1. postProcessBeanFactory(beanFactory) : 提供給AbstractApplicationContext的子類進行擴展,具體的子類,可以繼續向BeanFactory中再添加一些東西
  2. invokeBeanFactoryPostProcessors(beanFactory)執行BeanFactoryPostProcessor
    1. 此時在BeanFactory中會存在一個BeanFactoryPostProcessor:ConfigurationClassPostProcessor,它也是一個BeanDefinitionRegistryPostProcessor
    2. 第一階段
    3. 從BeanFactory中找到類型爲BeanDefinitionRegistryPostProcessor的beanName,也就是ConfigurationClassPostProcessor, 然後調用BeanFactory的getBean方法得到實例對象
    4. 執行ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法:
    1. 解析AppConfig類
    2. 掃描得到BeanDefinition並註冊
    3. 解析@Import,@Bean等註解得到BeanDefinition並註冊
    4. 詳細的看另外的筆記,專門分析了ConfigurationClassPostProcessor是如何工作的
    5. 在這裏,我們只需要知道在這一步會去得到BeanDefinition,而這些BeanDefinition中可能存在BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,所以執行完ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法後,還需要繼續執行其他BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    1. 執行其他BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    2. 執行所有BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
    3. 第二階段
    4. 從BeanFactory中找到類型爲BeanFactoryPostProcessor的beanName,而這些BeanFactoryPostProcessor包括了上面的BeanDefinitionRegistryPostProcessor
    5. 執行還沒有執行過的BeanFactoryPostProcessor的postProcessBeanFactory()方法
  1. 到此,所有的BeanFactoryPostProcessor的邏輯都執行完了,主要做的事情就是得到BeanDefinition並註冊到BeanFactory中
  2. registerBeanPostProcessors(beanFactory):因爲上面的步驟完成了掃描,這個過程中程序員可能自己定義了一些BeanPostProcessor,在這一步就會把BeanFactory中所有的BeanPostProcessor找出來並實例化得到一個對象,並添加到BeanFactory中去(屬性beanPostProcessors),最後再重新添加一個ApplicationListenerDetector對象(之前其實就添加了過,這裏是爲了把ApplicationListenerDetector移動到最後
  3. initMessageSource():如果BeanFactory中存在一個叫做"messageSource"的BeanDefinition,那麼就會把這個Bean對象創建出來並賦值給ApplicationContext的messageSource屬性,讓ApplicationContext擁有國際化的功能
  4. initApplicationEventMulticaster():如果BeanFactory中存在一個叫做"applicationEventMulticaster"的BeanDefinition,那麼就會把這個Bean對象創建出來並賦值給ApplicationContext的applicationEventMulticaster屬性,讓ApplicationContext擁有事件發佈的功能
  5. onRefresh():提供給AbstractApplicationContext的子類進行擴展,沒用
  6. registerListeners():從BeanFactory中獲取ApplicationListener類型的beanName,然後添加到ApplicationContext中的事件廣播器applicationEventMulticaster中去,到這一步因爲FactoryBean還沒有調用getObject()方法生成Bean對象,所以這裏要在根據類型找一下ApplicationListener,記錄一下對應的beanName
  7. finishBeanFactoryInitialization(beanFactory):完成BeanFactory的初始化,主要就是實例化非懶加載的單例Bean,單獨的筆記去講。
  8. finishRefresh():BeanFactory的初始化完後,就到了Spring啓動的最後一步了
    1. 設置ApplicationContext的lifecycleProcessor,默認情況下設置的是DefaultLifecycleProcessor
    2. 調用lifecycleProcessor的onRefresh()方法,如果是DefaultLifecycleProcessor,那麼會獲取所有類型爲Lifecycle的Bean對象,然後調用它的start()方法,這就是ApplicationContext的生命週期擴展機制
    3. 發佈ContextRefreshedEvent事件

執行BeanFactoryPostProcessor

  1. 執行通過ApplicationContext添加進來的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  2. 執行BeanFactory中實現了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  3. 執行BeanFactory中實現了Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  4. 執行BeanFactory中其他的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  5. 執行上面所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
  6. 執行通過ApplicationContext添加進來的BeanFactoryPostProcessor的postProcessBeanFactory()方法
  7. 執行BeanFactory中實現了PriorityOrdered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
  8. 執行BeanFactory中實現了Ordered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
  9. 執行BeanFactory中其他的BeanFactoryPostProcessor的postProcessBeanFactory()方法

Lifecycle的使用

Lifecycle表示的是ApplicationContext的生命週期,可以定義一個SmartLifecycle來監聽ApplicationContext的啓動和關閉:

@Component
public class ZhouyuLifecycle implements SmartLifecycle {

	private boolean isRunning = false;

	@Override
	public void start() {
		System.out.println("啓動");
		isRunning = true;
	}

	@Override
	public void stop() {
        // 要觸發stop(),要調用context.close(),或者註冊關閉鉤子(context.registerShutdownHook();)
		System.out.println("停止");
		isRunning = false;
	}

	@Override
	public boolean isRunning() {
		return isRunning;
	}
}

  

本系列文章來自圖靈學院周瑜老師分享,本博客整理學習並搬運

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