Spring源碼學習---IOC容器

前言

在以前的5個文章中咱們已經對 IOC, DI, AOP 和配置相關進行了一些瞭解,相信在此基礎上能夠幫助你們更好地去閱讀 Spring 的源碼git

源碼學習-IOC容器


① 注意事項

1.這裏的源碼版本號爲 version 5.1.3.RELEASEgithub

2.源碼獲取地址 github.com/spring-proj…spring

3.jdk要在1.8以上,spring5中大量使用了lambda表達式,而lambda表達式在1.8後開始支持編程

4.使用指南:spring.io/guides數據結構

5.各個版本的介紹:github.com/spring-proj…maven

6.Spring5.x的版本新特性:github.com/spring-proj…ide


② IOC的步驟整理(若是已經忘記,請回到 手寫Spring---IOC容器(1)

1.用戶配置bean定義 ---> 2.IOC容器加載bean定義 
    ---> 3.IOC容器建立bean實例 ---> 4.使用IOC容器
複製代碼

③ 使用Spring的入口和如何作好閱讀源碼的準備

使用Spring的入口爲:ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext(...Service.xml);

前期準備:
    建立一個maven工程,僅引入Spring-Context包(注意版本號)便可
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.3.RELEASE</version>
    </dependency>
複製代碼

④ 圍繞 ApplicationContext 咱們須要瞭解的問題(會先大體闡述,還並未涉及具體實現)

1. ApplicationContext 就是IOC容器

內部隱藏的BeanFactory是不多被關注的,提供給用戶直觀看到的就是ApplicationContext,我使用的是IDEA,能夠點擊類後打開navigate選項---Type Hierarchy---選擇SuperTypes Hierarchy,觀看它的繼承體系,如下是它做爲一個接口,又繼承了什麼接口post

既然它繼承了上面6個接口,那它一定會有這6個接口相關的行爲學習

2. ApplicationContext 所繼承的接口

EnvironmentCapable:取環境相關的參數,.properties文件
ListableBeanFactory:提供BeanFactorys行爲
HierarchicalBeanFactory:父子容器
    ---提供bean分層管理的方式
        且父容器沒法訪問子容器,子容器能夠訪問父容器,就好比只有兒子問老爸拿錢,沒有父親問兒子要錢的
MethodSource:國際化
ApplicationEventPublisher:應用的事件發佈,好比應用的開啓,結束,銷燬等等
ResourcePatternResolver:加載Resource
複製代碼

這些在後面咱們再展開來說···ui

3.繼承了 ApplicationContext 的接口

那如何先大體地查看這幅圖

先看 ConfigurableApplicationContext 這一大塊,
ConfigurableApplicationContext 如下全是抽象的,一直到 FileSystemXmlApplicationContext 
和 ClassPathXmlApplicationContext 兩個xml配置方式的具體實現,

而下面的 GenericApplicationContext 則是通用實現,其中包括了通用xml,靜態,動態語言Groovy和註解,
通用xml實現 GenericXmlApplicationContext 支持 FileSystemXmlApplicationContext 和 ClassPathXmlApplicationContext,
則不管放到文件系統或者classpath均可以

兩大塊 AbstractRefreshableApplicationContext 和 GenericApplicationContext 
都繼承了 AbstractApplicationContext
複製代碼
1、ConfigurableApplicationContext --- 可配置的 ApplicationContext

做爲一個接口確定加入了某些行爲,咱們參考圖左 structure 處,而後比照源碼片斷


① void addApplicationListener(ApplicationListener<?> listener);
此處加入了應用監聽器,確定是使用了觀察者模式,裏面發生的事件均可以往外提供發佈
經過這個 listener 就能夠獲取到
複製代碼

② void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

此處看名字是否會有熟悉的感受,若是沒有,能夠回顧下 手寫Spring---AOP面向切面編程(4)

在手寫AOP的時候,postProcessor已經被說起,講到了beanFactory如何可以靈活擴展,
可是咱們當時講的是BeanPostProcessor,是對bean的建立過程實現階段動態加強

那爲何如今這個是 BeanFactoryPostProcessor 呢,那就是它把工廠的建立過程也引入了各個階段
並且提供了各個階段支持動態加強的功能

ApplicationContext會幫咱們完成bean定義的加載,解析等一系列過程,在此過程當中咱們可能須要靈活加入一些處理
複製代碼

③ void refresh() throws BeansException, IllegalStateException;
這裏的refresh方法是刷新,刷新bean定義,IOC容器裏面的bean實例
複製代碼

2、AbstractApplicationContext --- 抽象的 ApplicationContext 的實現

此類中已經提供了不少的接口方法的實現,並且裏面的定義都廣泛具備了數據結構的支持

須要注意的是 registerBeanPostProcessors() 方法是保護類型的,只能供子類調用

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
複製代碼

以前說起到的 HierarchicalBeanFactory --- 父子容器在這裏也有體現

好比它的一個構造器,此時這裏父容器就已經給進來了

@Nullable
private ApplicationContext parent;

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
	this();
	setParent(parent);
}
複製代碼

此時再看看在setParent()方法中,大體就是取得父容器的環境參數,而後進行一個比較 instanceof 與合併 merge 的事情

@Override
public void setParent(@Nullable ApplicationContext parent) {
	this.parent = parent;
	if (parent != null) {
		Environment parentEnvironment = parent.getEnvironment();
		if (parentEnvironment instanceof ConfigurableEnvironment) {
			getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
		}
	}
}
複製代碼

而在此實現後的繼續深刻擴展就是 AbstractRefreshableApplicationContext 和 AbstractRefreshableConfigApplicationContext , 剛剛在 ConfigurableApplicationContext 的第 ③ 點不是說起了一個 refresh() 方法嗎,第一個就是可支持刷新的,第二個就是可支持刷新且又被配置的,再以後就是 xml 的了,咱們如今先不細說

3、GenericApplicationContext --- 通用的 ApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
複製代碼

此時往下看


1、構造方法

不難發現構造方法中,只有默認的 beanFactory 和父容器 parent 等做爲參數,並無說起咱們能夠給入 beanDefinition 的來源,好比 xml文件等等

private final DefaultListableBeanFactory beanFactory;

// 構造方法:

// 若是你沒有傳入beanFactory,那就是默認的
public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}

// 也能夠本身提供
public GenericApplicationContext(DefaultListableBeanFactory beanFactory) {
	Assert.notNull(beanFactory, "BeanFactory must not be null");
	this.beanFactory = beanFactory;
}
複製代碼

和上面同樣的套路,提供了setParent()方法

@Override
public void setParent(@Nullable ApplicationContext parent) {
	super.setParent(parent);
	this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
}
複製代碼

2、模板方法 refreshBeanFactory() --- 實現了父類的保護類型的方法
//---------------------------------------------------------------------
// Implementations of AbstractApplicationContext's template methods
//---------------------------------------------------------------------

/**
 * Do nothing: We hold a single internal BeanFactory and rely on callers
 * to register beans through our public methods (or the BeanFactory's).
 * @see #registerBeanDefinition
 */
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
	if (!this.refreshed.compareAndSet(false, true)) {
		throw new IllegalStateException(
				"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
	}
	this.beanFactory.setSerializationId(getId());
}

@Override
protected void cancelRefresh(BeansException ex) {
	this.beanFactory.setSerializationId(null);
	super.cancelRefresh(ex);
}
複製代碼

3、接口 BeanDefinitionRegistry 要求提供的方法,包括實現註冊bean定義等等


4、其餘

這樣下來,咱們能大體瞭解最外層的 ApplciationContext 是如何一步步加入哪些參數的


Finally

此篇只是大體地看了一下 ApplicationContext 的子類所擁有的一些東西,可能你們看起來會以爲一頭霧水,下一篇會結合實例去進行使用而後一步步進行分析,可能篇幅就會增長。

有人反映了篇幅過長的問題,因此如今秉承少吃多餐的原則去慢更,望多多總結,互相進步··

相關文章
相關標籤/搜索