第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Spring 工程執(zhí)行過程

1. 前言

Spring 框架是如何工作的?

本節(jié)目的在于幫助大家理解 Spring 框架底層干了什么事情。

在上一節(jié)中我們通過一個入門工程簡單地體驗了一把 Spring 的使用。

我們發(fā)現(xiàn),通過構造一個 ClassPathXmlApplicationContext 對象,加載項目的 applicationContext.xml 文件,確實可以實例化對象。

疑問導出

而腦海中不禁有一個想法… Spring 如何初始化對象的實例的?我們又如何從容器中獲取得到對象的實例的呢?

帶著疑問… 開啟本節(jié)的源碼和原理之旅。

2. 容器初始化

回顧代碼:

public static void main(String[] args) {
    ApplicationContext context =
            new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    UserService service = (UserService) context.getBean("userService");
    service.saveUser();
}

在上面的代碼中可以得知 Spring 的容器是 ApplicationContext,那么它到底是什么東西呢?先跟我一起追蹤一下它的角色。
官方文檔
圖片描述
慕課解釋

簡單翻譯過來就是 ApplicationContext 是一個 接口,是 BeanFactory 這個接口的子接口,它擴展了 BeanFactory 這個接口,提供了額外附加的功能。
BeanFactory 是管理 bean 對象的容器的根接口,大家了解下就好,我們是針對它的子接口 ClassPathXmlApplicationContext 做的實例化,目的是加載項目中的 Spring 的配置文件,使 Spring 來管理我們定義的 bean 對象。

疑問導出
那么我們的問題是…ClassPathXmlApplicationContext 對象實例化之后,干了哪些事情呢?

2.1 容器初始化執(zhí)行動作

applicationContext 實例化執(zhí)行代碼邏輯 。
我們追蹤下源碼,發(fā)現(xiàn) ClassPathXmlApplicationContext 初始化的時候,它做了一系列的事情。源碼如下:
圖片描述

代碼解釋:

  1. 是初始化 ClassPathXmlApplicationContext 對象執(zhí)行的有參構造;
  2. 加載項目下的 xml 配置文件;
  3. 調用 refresh 刷新容器的方法 bean 的實例化就在這個方法中。

繼續(xù)跟蹤:

2.2 容器初始化 bean 對象動作

下面是從源碼中粘貼的部分代碼

圖片描述
步驟闡述:

對于我們而言 這些英文看起來很吃力… 放輕松大家,我們只關注對我們理解流程有用的代碼:

  1. 1 的位置:是準備刷新,那么 Spring 只是設置刷新的標記,加載了外部的 properties 屬性文件;
  2. 2 的位置:是準備 bean 工廠對象;
  3. 3 的位置:這一步驟就加載了配置文件中的所有 bean 標簽,但是并沒有對他們進行實例化;
  4. 4 的位置:完成此上下文的 bean 工廠的初始化,初始化所有剩余的單例 bean。(Spring 中默認加載的 bean 就是單例模式后面生命周期會講)
  5. 最后的位置:完成容器的刷新,也就是所有的 bean 初始化完成。
		//這里粘貼一部分初始化代碼的邏輯 幫助大家理解
		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
		// Trigger initialization of all non-lazy singleton beans...
		//所有非懶加載的單例bean的觸發(fā)器初始化。。。
		for (String beanName : beanNames) {
		  ...//省略循環(huán)的代碼
		}

OK 上面就是加載配置文件后 Spring 框架做的所有事情,當然實際底層涉及的東西 更多,但是我們沒有必要深究,畢竟我們是理解過程,不是追求實現(xiàn)。

疑問導出:

我們整理了 Spring 初始化 bean 對象的過程,那么如果容器中確實存在了 bean 的實例,我們是如何獲取得到的呢?

3. 容器中獲取對象的過程

還是先看下我們獲取容器對象的代碼:

public static void main(String[] args) {
    ApplicationContext context =
            new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    UserService service = (UserService) context.getBean("userService");
    service.saveUser();
}

代碼分析

context.getBean 的方法是通過 bean 標簽里的 id 來從容器中獲取,那么我們看下源碼 :
在父類 AbstractApplicationContext 中有對 getBean 方法的實現(xiàn)。

	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

追蹤父類方法
最終通過我們層層追蹤,我們在 AbstractAutowireCapableBeanFactory 中發(fā)現(xiàn)這樣的一段代碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
		//...
		//省略大量方法內部代碼
        //...
		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            //給實例中的屬性賦值
			populateBean(beanName, mbd, instanceWrapper);
            //真實實例化對象
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		//...
        //繼續(xù)省略大量方法
        //...
		// Register bean as disposable.
		try {
            //將實例化后的對象放入容器中
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}
		//返回實例化后的對象實例
		return exposedObject;
	}

上面源碼中我們可以看到: 對象實例的獲取好像是在獲取的時候執(zhí)行的 doCreateBean,那么之前記載的 xml 文件不是實例過了嗎?稍微解釋下:加載文件時候的實例化操作,其實是實例化了一個 Spring 框架提供的對象,作用是對于我們 bean 對象做描述,這里才是真實的實例化動作。我們再看看 registerDisposableBeanIfNecessary 這個方法做的是什么。

public void registerDisposableBean(String beanName, DisposableBean bean) {
		synchronized (this.disposableBeans) {
			this.disposableBeans.put(beanName, bean);
		}
	}

圖片描述
結論
一切真相大白。它其實就是一個 map 集合 ,這個 map 集合的 key 就是我們定義的 bean 的 id 或者 bean 的 name ,那么值就是對象的實例。

4. 小結

本章節(jié) 帶著大家梳理了一下 Spring 初始化 bean 和獲取 bean 的流程:

  1. Spring 框架通過 ResourceLoader 加載項目的 xml 配置文件;
  2. 讀取 xml 的配置信息 變成對象存儲,但未實例化;
  3. 通過 bean 工廠處理器對 bean 做實例化,存儲到一個 map 集合中默認是單例;
  4. 獲取對象 通過 xml 文件中 bean 的 id 從 map 集合中通過 get (key) 獲取。

羅馬不是一天建成的 ,書山有路勤為徑…