Spring 源碼第六彈!容器的始祖 DefaultListableBeanFactory

Spring 源碼繼續開整!java

在 XML 文件解析流程一文中,鬆哥和你們分享了 Spring 中配置文件的加載方式,若是小夥伴們還沒看過,必定先看一下,這有助於更好的理解本文,傳送門:Spring 源碼第一篇開整!配置文件是怎麼加載的?bootstrap

還記得該篇文章中的代碼嗎?緩存

XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = factory.getBean(User.class);
System.out.println("user = " + user);
複製代碼

當 ClassPathResource 將文件以 IO 流的方式輸出後,接下來就是構造 XmlBeanFactory ,XmlBeanFactory 功能有限,它的大部分功能都在它的父類 DefaultListableBeanFactory 中實現了,而 DefaultListableBeanFactory 也至關因而容器的始祖,爲何這麼說呢?咱們今天就來講一說這個話題。併發

本文是 Spring 源碼解讀第七篇,閱讀本系列前面文章有助於更好的理解本文:app

  1. Spring 源碼解讀計劃
  2. Spring 源碼第一篇開整!配置文件是怎麼加載的?
  3. Spring 源碼第二彈!XML 文件解析流程
  4. Spring 源碼第三彈!EntityResolver 是個什麼鬼?
  5. Spring 源碼第四彈!深刻理解 BeanDefinition
  6. 手把手教你搭建 Spring 源碼分析環境

1.DefaultListableBeanFactory

要說 XmlBeanFactory 就不得不先說它的父類 DefaultListableBeanFactory,由於 XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了,XmlBeanFactory 只是對 IO 流的讀取作了一些定製而已。ide

DefaultListableBeanFactory 是一個完整的、功能成熟的 IoC 容器,若是你的需求很簡單,甚至能夠直接使用 DefaultListableBeanFactory,若是你的需求比較複雜,那麼經過擴展 DefaultListableBeanFactory 的功能也能夠達到,能夠說 DefaultListableBeanFactory 是整個 Spring IoC 容器的始祖。源碼分析

咱們先來看一下 DefaultListableBeanFactory 的繼承關係:post

從這張類的關係圖中能夠看出,DefaultListableBeanFactory 實際上也是一個集大成者。在 Spring 中,針對 Bean 的不一樣操做都有不一樣的接口進行規範,每一個接口都有本身對應的實現,最終在 DefaultListableBeanFactory 中將全部的實現匯聚到一塊兒。從這張類的繼承關係圖中咱們大概就能感覺到 Spring 中關於類的設計是多麼厲害,代碼耦合度很是低。ui

這些類,在本系列後面的介紹中,大部分都會涉及到,如今我先大概介紹一下每一個類的做用,你們先混個臉熟:this

  1. BeanFactory:這個接口看名字就知道是一個 Bean 的工廠,BeanFactory 接口定義了各類獲取 Bean 的方法、判斷 Bean 是否存在、判斷 Bean 是否單例等針對 Bean 的基礎方法。
  2. ListableBeanFactory:這個接口繼承自 BeanFactory,在 BeanFactory 的基礎上,擴展了 Bean 的查詢方法,例如根據類型獲取 BeanNames、根據註解獲取 BeanNames、根據 Bean 獲取註解等。
  3. AutowireCapableBeanFactory:該接口繼承自 BeanFactory,在 BeanFactory 的基礎上,提供了 Bean 的建立、配置、注入、銷燬等操做。有時候咱們須要本身手動注入 Bean 的時候,能夠考慮經過實現該接口來完成。AutowireCapableBeanFactory 在 Spring Security 中有一個重要的應用就是 ObjectPostProcessor,這個鬆哥將在 Spring Security 系列中和你們詳細介紹。
  4. HierarchicalBeanFactory:該接口繼承自 BeanFactory,並在 BeanFactory 基礎上添加了獲取 parent beanfactory 的方法。
  5. SingletonBeanRegistry:這個接口定義了對單例 Bean 的定義以及獲取方法。
  6. ConfigurableBeanFactory:這個接口主要定了針對 BeanFactory 的各類配置以及銷燬的方法。
  7. ConfigurableListableBeanFactory:這是 BeanFactory 的配置清單,這裏定義了忽略的類型、接口,經過 Bean 的名稱獲取 BeanDefinition 、凍結 BeanDefinition 等。
  8. AliasRegistry:這個接口定義了對 alias 的註冊、移除、判斷以及查詢操做。
  9. SimpleAliasRegistry:這個類實現了 AliasRegistry 接口並實現了它裏邊的方法,SimpleAliasRegistry 使用 ConcurrentHashMap 作載體,實現了對 alias 的註冊、移除判斷以及查詢操做。
  10. DefaultSingletonBeanRegistry:這個類基於 Java 中的集合,對 SingletonBeanRegistry 接口進行了實現。
  11. FactoryBeanRegistrySupport:該類繼承自 DefaultSingletonBeanRegistry,並在 DefaultSingletonBeanRegistry 的基礎上,增長了獲取 FactoryBean 類型、移除 FactoryBean 緩存的方法等等操做。
  12. AbstractBeanFactory:實現了 ConfigurableBeanFactory 接口並繼承自 FactoryBeanRegistrySupport,在 AbstractBeanFactory 中對 ConfigurableBeanFactory 中定義的方法進行了實現。
  13. AbstractAutowireCapableBeanFactory:該類繼承自 AbstractBeanFactory 並對 AutowireCapableBeanFactory 接口中定義的方法進行了落地實現。
  14. BeanDefinitionRegistry:這個接口繼承自 AliasRegistry 接口,並增長了一系列針對 BeanDefinition 的註冊、移除、查詢、判斷等方法。
  15. 最後的 DefaultListableBeanFactory 天然就具有了上面全部的功能。

上面的內容可能看的你們眼花繚亂,鬆哥這裏經過幾個簡單實際的例子,來帶你們使用一下 DefaultListableBeanFactory 的功能,可能你們的理解就比較清晰了。

DefaultListableBeanFactory 做爲一個集大成者,提供了很是多的功能,咱們一個一個來看。

2.代碼改造

首先文章中一開始的三行代碼咱們能夠對其略加改造,由於咱們已經說了 XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了,XmlBeanFactory 只是對 IO 流的讀取作了一些定製而已,文件的讀取主要是經過 XmlBeanDefinitionReader 來完成的(本系列前面文章已經講過),咱們能夠對文章一開始的三行代碼進行改造,以便更好的體現「XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了」:

ClassPathResource res=new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
User user = factory.getBean(User.class);
System.out.println("user = " + user);
複製代碼

使用前四行代碼代替 XmlBeanFactory,這樣 XmlBeanFactory 的功能是否是就很明確了?就是前四行代碼的功能。

3.動態註冊 Bean

動態註冊 Bean,這是 DefaultListableBeanFactory 的功能之一,不過準確來講應該是動態註冊 BeanDefinition 。

咱們先來看一個簡單的例子:

DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
複製代碼

首先咱們本身手動構建一個 DefaultListableBeanFactory 對象。固然也可使用前面的 XmlBeanFactory。

而後再手動構建一個 GenericBeanDefinition。在前面的文章中,鬆哥和你們講過,如今默認使用的 BeanDefinition 就是 GenericBeanDefinition,因此這裏咱們本身也手動構建一個 GenericBeanDefinition。有了 GenericBeanDefinition 以後,咱們設置相關的類和屬性。

接下來再將 userBeanDefinition 註冊到 defaultListableBeanFactory。註冊完成以後,咱們就能夠從 defaultListableBeanFactory 中獲取相應的 Bean 了。

這裏說一句題外話,但願你們在閱讀本系列每一篇文章的時候,可以將本系列先後文章聯繫起來一塊兒理解,這樣會有不少意料以外的收穫。例如上面的,咱們既能夠聲明一個 DefaultListableBeanFactory,也能夠聲明一個 XmlBeanFactory,那你大概就能據此推斷出 XmlBeanFactory 的主要目的可能就是對資源文件進行讀取和註冊。

那麼究竟是怎麼註冊的呢?咱們來看一下 defaultListableBeanFactory.registerBeanDefinition 方法的定義:

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");
	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if (existingDefinition != null) {
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(existingDefinition)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			synchronized (this.beanDefinitionMap) {
				this.beanDefinitionMap.put(beanName, beanDefinition);
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				removeManualSingletonName(beanName);
			}
		}
		else {
			// Still in startup registration phase
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	if (existingDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
	else if (isConfigurationFrozen()) {
		clearByTypeCache();
	}
}
複製代碼

registerBeanDefinition 方法是在 BeanDefinitionRegistry 接口中聲明的,DefaultListableBeanFactory 類實現了 BeanDefinitionRegistry 接口,並實現了該方法,咱們來看分析下該方法:

  1. 首先對傳入的 beanDefinition 對象進行校驗,這也是註冊前的最後一次校驗,不過這個時候 BeanDefinition 對象已經到手了,因此這個校驗並不是 XML 文件校驗,這裏主要是對 methodOverrides 的校驗。
  2. 接下來會根據 beanName 從 beanDefinitionMap 中獲取 BeanDefinition,看看當前 Bean 是否已經定義過了。beanDefinitionMap 是一個 Map 集合,這個集合中 key 是 beanName,value 是 BeanDefinition 對象。
  3. 若是 BeanDefinition 已經存在了,那麼接下來會判斷是否容許 BeanDefinition 覆蓋,若是不容許,就直接拋出異常(不知道小夥伴們有沒有印象,在鬆哥前面的 OAuth2 系列教程中,常常須要配置容許 BeanDefinition 的覆蓋,就是由於這個緣由,公衆號【江南一點雨】後臺回覆 OAuth2 獲取該教程),若是容許 BeanDefinition 的覆蓋,那就向 beanDefinitionMap 中再次存一次值,覆蓋以前的值。
  4. 若是 BeanDefinition 不存在,那就直接註冊。直接註冊分兩種狀況:項目已經運行了和項目還沒運行。
  5. 若是項目已經運行,因爲 beanDefinitionMap 是一個全局變量,可能存在併發問題,因此要加鎖處理。不然就直接註冊,所謂的註冊就是把對象存入 beanDefinitionMap 中,同時將 beanName 都存入 beanDefinitionNames 集合中。

這即是 registerBeanDefinition 方法的工做流程。

有小夥伴會說,這個方法從頭至尾都是 BeanDefinition,跟 Bean 有什麼關係呢?

咋一看確實好像和 Bean 沒有直接關係。

其實這涉及到另一個問題,就是 Bean 的懶加載。這個時候先把 BeanDefinition 定義好,等到真正調用 Bean 的時候,纔會去初始化 Bean。咱們能夠在 User 類的構造方法中打印日誌看下,以下:

public class User {
    private String username;
    private String address;

    public User() {
        System.out.println("--------user init--------");
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
複製代碼

從下圖能夠看到,當 BeanDefinition 註冊完成後,User 並無初始化,等到 getBean 方法被調用的時候,User 才初始化了。

須要注意的是,咱們平常開發中使用的 ApplicationContext 並不是懶加載,這個在鬆哥的 Spring 入門視頻中能夠看到效果【www.bilibili.com/video/BV1Wv…】,具體原理鬆哥將在本系列後面的文章中和你們分享。

那麼若是不想懶加載該怎麼辦呢?固然有辦法。

4.提早註冊 Bean

在 DefaultListableBeanFactory 中還有一個 preInstantiateSingletons 方法能夠提早註冊 Bean,該方法是在 ConfigurableListableBeanFactory 接口中聲明的,DefaultListableBeanFactory 類實現了 ConfigurableListableBeanFactory 接口並實現了接口中的方法:

@Override
public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}
	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					final FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
										((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				getBean(beanName);
			}
		}
	}
	// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else {
				smartSingleton.afterSingletonsInstantiated();
			}
		}
	}
}
複製代碼

preInstantiateSingletons 方法的總體邏輯比較簡單,就是遍歷 beanNames,對符合條件的 Bean 進行實例化,並且你們注意,這裏所謂的提早初始化其實就是在咱們調用 getBean 方法以前,它本身先調用了一下 getBean。

咱們能夠在案例中手動調用該方法:

DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
defaultListableBeanFactory.preInstantiateSingletons();
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
複製代碼

此時在調用 getBean 方法以前,User 就已經初始化了,以下圖:

5.getBean

DefaultListableBeanFactory 中另一個重量級方法就是 getBean 了。不過 getBean 方法的真正實現是在 DefaultListableBeanFactory 的父類 AbstractBeanFactory 中,具體的實現方法是 doGetBean,原本想和你們子在這裏聊一聊這個問題,可是發現這是一個很是龐大的問題,BeanFactory 和 FactoryBean 都還沒和你們分享,因此這個話題咱們仍是暫且押後,一個點一個點來。

6.小結

好啦,今天就先說這麼多,每篇源碼我都儘可能配置套一些小案例來演示,這樣避免你們看的太枯燥了,咱們下週繼續~

若是你們以爲有收穫,記得點個在看鼓勵下鬆哥哦~

相關文章
相關標籤/搜索