深入剖析Spring(四):IOC核心思想(源碼分析篇)

Spring框架能發展至今併成爲最主流的框架必然有它的道理,對於我們學習者來說理應儘量的去汲取前輩們知識,我們不能被現今便捷的框架遮蔽了自己的雙眼也許你離開了這些框架你可能什麼都不是,因爲我們已經是站在了巨人的肩膀上。

一、 前言

記錄本系列的文章主要目的是爲了自己的學習梳理,同時也希望各位業界的前輩們能夠幫忙指點迷津。
本篇內容主要是理解一下IOC運行流程以及源碼。

二、IOC(Inversion of Control)

控制反轉:所謂控制反轉就是將我們代碼中需要實現的對象創建、依賴的代碼,反轉給容器來幫忙實現。要完成此操作必然需要一個容器,同時需要某種描述來讓容器知道需要創建的對象與對象的關係,這個描述具體的體現就是\color{#FF0000}{配置文件}
既然說到了配置文件那我們來思考幾個問題:
1.如何描述對象和對象的關係?
----> 我們從xml、properties、yml等語義話配置文件都可以表示?
2.文件存放在哪?
----> 可以存在classpath、filesystem、servletContext也可以是網絡資源。
3.配置文件不同,我們如何統一解析?
----> 我們首先需要定義一個統一的對象定義,所有資源都需要轉換成改定義格式。

2.1 BeanFactory

SpirngBean的創建是典型工廠模式,一系列的工廠模式,爲IOC容器的開發和管理提供了許多便捷的基礎服務,我們先來看看BeanFactory的類圖看一看他們之間的關係,如下:
在這裏插入圖片描述
我們可以看到BeanFactory作爲頂層的接口類,它的裏面定義很多IOC容器的基本規範,它有三個重要的子類,AutowireCapableBeanFactoryHierarchicalBeanFactoryListableBeanFactory並且它們最終的實現類都是DefaultListableBeanFactory,他實現了所有的接口。
那爲何要定義這麼多的接口?
個人的理解是單一職責原則,每個接口都有他自己的使用場合,爲了區分在Spring內部操作過程中對象的傳遞和轉化的過程。說白了就是方便管理和維護。
比如說:ListableBeanFactory表示這些Bean是可序列化的,HierarchicalBeanFactory 表示這些Bean是有繼承關係的,每個Bean也可能會有父類Bean。AutowireCapableBeanFactory 表示這些Bean的自動裝配規則。這三個接口共用定義組成了Bean的集合、Bean之間的關係、以及Bean的行爲。
我們來看看BeanFactory的源碼:

public interface BeanFactory {

    //轉義標識符,如果需要得到工廠本身,需要轉義
    String FACTORY_BEAN_PREFIX = "&";


    //根據bean的名字,獲取在IOC容器中得到bean實例
    Object getBean(String name) throws BeansException;


    //根據bean的名字和Class類型來得到bean實例,增加了類型安全驗證機制。
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;


    Object getBean(String name, Object... args) throws BeansException;


    <T> T getBean(Class<T> requiredType) throws BeansException;


    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;


    //提供對bean的檢索,看看是否在IOC容器有這個名字的bean
    boolean containsBean(String name);


    //並同時判斷這個bean是不是單例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    //判斷是不是原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    /**
     *
     * 根據beanName判斷是否有指定類型的匹配
     * @param name beanName
     * @param typeToMatch 需要匹配的目標類型
     * 例如:
     *   name="time",  typeToMatch=Date.Class
     *   如果time類型是Date類型返回true
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    //同上一樣
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;


    //得到bean實例的Class類型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;


    //得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來
    String[] getAliases(String name);
}

在BeanFactory中對IOC做了基本的行爲定義,根本不關心你的Bean是如何定義和加載的。
而我們需要知道工廠是如何生產對象的,就需要看IOC的具體實現類,Spring也提供了許多IOC容器的實現,例如:GenericApplicationContext 、ClasspathXmlApplicationContext ,但是我們最常用的還是ApplicationContext,爲什麼呢?
ApplicationContext是spring提供的一個比較高級點的IOC容器,它除了能夠提供IOC的基本功能外,還爲用戶提供了許多額外的功能擴展:
1、支持信息源,可以實現國際化。(實現MessageSource接口)
2、訪問資源。(實現ResourcePatternResolver接口)
3、支持應用事件。(實現ApplicationEventPublisher接口)
這三點目前小編都還沒有系統的接觸到,先給大家看看,希望大佬能夠提供一下資源( - -!),在此感謝

2.2 BeanDefinition

SpirngIOC管理了我們定義的各種Bean對象以及它們之間的相互關係,Bean對象在spring中是以BeanDefinition來描述的,在Spring容器啓動的過程中,會將Bean解析成BeanDefinition結構,我們先來看看它的類圖,如下:
在這裏插入圖片描述
在這我解析一下BeanDefinition接口定義的屬性,在接口內部定義了非常多的屬性和方法,類名、scope、屬性、構造函數參數列表、依賴的bean、是否是單例類、是否是懶加載之類的,本質就是將Bean封裝到BeanDefinition中在後續操作中就是對BeanDefinition操作,可以根據裏面的類名、構造函數、構造函數參數,使用反射進行對象創建。

2.3 BeanDefinitionReader

Bean的解析過程實際上還是比較複雜的,功能劃分粒度很細,因爲這個地方需要被擴展的地方很多,所以必須保證有足夠的靈活性來面對不同的變化,Bean的解析本質上就是對Spring配置文件的解析。這個解析的過程通過BeanDefinitionReader來完成,我們來看看類圖:
在這裏插入圖片描述

三、IOC初始化流程

在上一篇文章MVC介紹的DispatcherServlet類中,我們所有的初始化工作都是放在init()中完成,那麼這個init()方法是誰的方法呢?往上追尋HttpServletBean類中找到了我們的答案:

public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				//定位資源
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				//加載配置信息
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
		// 真正執行初始化邏輯的入口
		initServletBean();
		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

我們發現了initServletBean()方法,在init()方法中,真正完成初始化容器動作的邏輯其實在initServletBean()方法中,我們追尋此方法,在FrameworkServlet中完成了對該方法的實現,請看源碼:

protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

在此方法並沒有過於複雜的邏輯,就不做啥解釋了,我們發現有initWebAppplicationContext(),是不是有點熟悉的感覺?繼續跟進

protected WebApplicationContext initWebApplicationContext() {

		//先從ServletContext中獲得父容器WebAppliationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		//聲明子容器
		WebApplicationContext wac = null;

		//建立父、子容器之間的關聯關係
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					//這個方法裏面調用了AbatractApplication的refresh()方法
					//refresh()方法是真正的IOC初始化的入口
					//模板方法,規定IOC初始化基本流程
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//先去ServletContext中查找Web容器的引用是否存在,並創建好默認的空IOC容器
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		//給上一步創建好的IOC容器賦值
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		//觸發onRefresh方法
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

找到重點方法configureAndRefreshWebApplicationContext()跟進

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

觀察以上兩處代碼在initWebApplicationContext()方法中調用了configAndRefreshWebApplicationContext()方法,在此方法中調用了refresh()方法,這個是真正啓動IOC 容器的入口,IOC 容器初始化以後,最後調用了DispatcherServlet的onRefresh()方法,在onRefresh()方法中又是直接調用initStrategies()方法初始化SpringMVC的九大組件:

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	//初始化策略
	protected void initStrategies(ApplicationContext context) {
		//多文件上傳的組件
		initMultipartResolver(context);
		//初始化本地語言環境
		initLocaleResolver(context);
		//初始化模板處理器
		initThemeResolver(context);
		//handlerMapping
		initHandlerMappings(context);
		//初始化參數適配器
		initHandlerAdapters(context);
		//初始化異常攔截器
		initHandlerExceptionResolvers(context);
		//初始化視圖預處理器
		initRequestToViewNameTranslator(context);
		//初始化視圖轉換器
		initViewResolvers(context);
		//
		initFlashMapManager(context);
	}

四、基於配置文件的IOC容器初始化

4.1定位文件

IOC 容器的初始化包括BeanDefinition的Resource定位、加載和註冊這三個基本的過程,在上文介紹了spring提供了很多IOC容器實現,我們就以ApplicationContext爲例,因爲我們最熟悉它使用的也最多,因爲在web項目中使用的XmlWebApplicationContext以及ClasspathXmlApplicationContext
就來自這個體系。
ApplicationContext允許上下文嵌套,通過保持父上下文可以維持一個上下文體系。對於Bean的查找
可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣爲不同的Spring應用提供了一個共享的Bean定義環境。

ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");

不知大家對此方法是否還有映像,在我們最初學習spring的時候就是通過此方法指定配置文件,然後來測試各種Bean的注入啊什麼的。我們這裏也從此深入。
查看其構造函數的調用

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

查看此重載方法

public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

也就是說實際上了是調用了此方法。
其實不光ClassPathXmlApplicationContext像還有 AnnotationConfigApplicationContext 、 FileSystemXmlApplicationContext 、XmlWebApplicationContext等都繼承自父容器AbstractApplicationContext主要用到了裝飾器模式和策略模式,最終都是調用refresh()方法。

4.2 獲得文件路徑

通 過分析ClassPathXmlApplicationContext的源代碼可以知道 ,在創建
ClassPathXmlApplicationContext容器時,構造方法做以下兩項重要工作:首先,調用父類容器的構造方法(super(parent)方法)爲容器設置好Bean資源加載器。然後,再調用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設置Bean配置信息的定位路徑。通過追蹤ClassPathXmlApplicationContext的繼承體系 , 發現其父類的 類AbstractApplicationContext中初始化IOC容器所做的主要部分源碼如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	//靜態初始化塊,在整個容器創建過程中只執行一次
	static {
		
		//爲了避免應用程序在Weblogic8.1關閉時出現類加載異常加載問題,加載IoC容
		//器關閉事件(ContextClosedEvent)類
		ContextClosedEvent.class.getName();
	}
	public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver(); } 
	public AbstractApplicationContext(@Nullable ApplicationContext parent) { this(); setParent(parent); } 
	//獲取一個 Spring Source 的加載器用於讀入 Spring Bean 配置信息 
	protected ResourcePatternResolver getResourcePatternResolver() { //AbstractApplicationContext 繼承 DefaultResourceLoader,因此也是一個資源加載器 //Spring 資源加載器,其 getResource(String location)方法用於載入資源 
	return new PathMatchingResourcePatternResolver(this); }
/**省略以下代碼*/
}

AbstractApplicationContext 的默認構造方法中有調用 PathMatchingResourcePatternResolver 的
構造方法創建Spring資源加載器:

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		//設置Spring的資源加載器
		this.resourceLoader = resourceLoader;
	}

在設置容器的資源加載器之後,接下來回到 ClassPathXmlApplicationContext構造方法中繼續 執行setConfigLocations()方法通過調用其父類AbstractRefreshableConfigApplicationContext的方法進行對Bean配置信息的定位,該方法的源碼如下:

	//解析Bean定義資源文件的路徑,處理多個資源文件字符串數組
	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				// resolvePath爲同一個類中將字符串解析爲路徑的方法
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

從這裏我們可以知道我們可以使用字符串來配置多個SpringBean的配置信息,也可以使用字符串數組
即:

ClassPathResource res = new ClassPathResource("a.xml,b.xml");
ClassPathResource res =new ClassPathResource(new String[]{"a.xml","b.xml"});

至此,SpringIOC 容器在初始化時將配置的Bean配置信息定位爲Spring封裝的Resource完成。

4.3啓動

上文提到過IOC的容器初始化真正的邏輯是在refresh() 中,而該又是實際上又是一個模板方法,只是規定了IOC啓動的流程,具體的邏輯還是由其子類來實現的。它對 Bean 配置資源進行載入
ClassPathXmlApplicationContext 通過調用其父類 AbstractApplicationContext 的 refresh()函數啓
動整個IOC 容器對Bean定義的載入過程,現在我們來詳細看看refresh()中的邏輯處理:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//1、調用容器準備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識
			prepareRefresh();
			//2、告訴子類啓動refreshBeanFactory()方法,Bean定義資源文件的載入從
			//子類的refreshBeanFactory()方法啓動
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			//3、爲BeanFactory配置容器特性,例如類加載器、事件處理器等
			prepareBeanFactory(beanFactory);
			try {
				//4、爲容器的某些子類指定特殊的BeanPost事件處理器
				postProcessBeanFactory(beanFactory);
				
				//5、調用所有註冊的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);
				
				//6、爲BeanFactory註冊BeanPost事件處理器.
				//BeanPostProcessor是Bean後置處理器,用於監聽容器觸發的事件
				registerBeanPostProcessors(beanFactory);

				//7、初始化信息源,和國際化相關.
				initMessageSource();

				//8、初始化容器事件傳播器.
				initApplicationEventMulticaster();

				//9、調用子類的某些特殊Bean初始化方法
				onRefresh();

				//10、爲事件傳播器註冊事件監聽器.
				registerListeners();

				//11、初始化所有剩餘的單例Bean
				finishBeanFactoryInitialization(beanFactory);

				//12、初始化容器的生命週期事件處理器,併發布容器的生命週期事件
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				//13、銷燬已創建的Bean
				destroyBeans();

				//14、取消refresh操作,重置容器的同步標識。
				cancelRefresh(ex);

				throw ex;
			}

			finally {
	
				//15、重設公共緩存
				resetCommonCaches();
			}
		}
	}

refresh()方法主要爲 IOC 容器 Bean 的生命週期管理提供條件,Spring IOC 容器載入 Bean 配置信息
從 其 子 類 容 器 的 refreshBeanFactory() 方 法 啓 動 , 所 以 整 個 refresh() 中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();從這以後代碼的
都是註冊容器的信息源和生命週期事件,我們前面說的載入就是從這句代碼開始啓動。
refresh()方法的主要作用是:
在創建 IOC 容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的 IOC容器。它類似於對IOC 容器的重啓,在新建立好的容器中對容器進行初始化,對Bean配置資源進行載入。

4.4創建容器

obtainFreshBeanFactory()方法調用子類容器的 refreshBeanFactory()方法,啓動容器載入Bean配置
信息的過程,代碼如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//這裏使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現調用子類容器的refreshBeanFactory()方法
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

AbstractApplicationContext 類中只抽象定義了 refreshBeanFactory()方法,容器真正調用的是 其子類 AbstractRefreshableApplicationContext 實現的 refreshBeanFactory()方法,方法的源 碼如下:

protected final void refreshBeanFactory() throws BeansException {
		//如果已經有容器,銷燬容器中的bean,關閉容器
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//創建IOC容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//對IOC容器進行定製化,如設置啓動參數,開啓註解的自動裝配等
			customizeBeanFactory(beanFactory);
			//調用載入Bean定義的方法,主要這裏又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現調用子類容器
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

在這個方法中,先判斷 BeanFactory 是否存在,如果存在則先銷燬 beans並關閉beanFactory,接着
創建DefaultListableBeanFactory,並調用loadBeanDefinitions(beanFactory)裝載bean定義。

4.5載入配置路徑

上面類中AbstractRefreshableApplicationContext 中定義了抽象方法loadBeanDefinitions ,容器真正調用的是其子類 AbstractXmlApplicationContext 對該方法的實現,AbstractXmlApplicationContext
的主要源碼如下:

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
			throws BeansException, IOException;

loadBeanDefinitions() 方 法 同 樣 是 抽 象 方 法 , 是 由 其 子 類 實 現 的 , 也 即 在
AbstractXmlApplicationContext中。

	//實現父類抽象的載入Bean定義方法
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		
		//創建XmlBeanDefinitionReader,即創建Bean讀取器,並通過回調設置到容器中去,容  器使用該讀取器讀取Bean定義資源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		//爲Bean讀取器設置Spring資源加載器,AbstractXmlApplicationContext的
		//祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個資源加載器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//爲Bean讀取器設置SAX xml解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
		//當Bean讀取器讀取Bean定義的Xml資源文件時,啓用Xml的校驗機制
		initBeanDefinitionReader(beanDefinitionReader);
		//Bean讀取器真正實現加載的方法
		loadBeanDefinitions(beanDefinitionReader);
	}

注意最後一行的調用

//Xml Bean讀取器加載Bean定義資源
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//獲取Bean定義資源的定位
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位
			//的Bean定義資源
			reader.loadBeanDefinitions(configResources);
		}
		//如果子類中獲取的Bean定義資源定位爲空,則獲取FileSystemXmlApplicationContext構造方法中setConfigLocations方法設置的資源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//Xml Bean讀取器調用其父類AbstractBeanDefinitionReader讀取定位
			//的Bean定義資源
			reader.loadBeanDefinitions(configLocations);
		}
	}

查看上方法中的getConfigResources()方法

	//這裏又使用了一個委託模式,調用子類的獲取Bean定義資源定位的方法
	//該方法在ClassPathXmlApplicationContext中進行實現
	@Nullable
	protected Resource[] getConfigResources() {
		return null;
	}
4.6分配路徑處理策略

在XmlBeanDefinitionReader的抽象父類AbstractBeanDefinitionReader中定義了載入過程,loadBeanDefinitions()方法源碼如下:

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//獲取在IoC容器初始化過程中設置的資源加載器
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//將指定位置的Bean定義資源文件解析爲Spring IOC容器封裝的資源
				//加載多個指定位置的Bean定義資源文件
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			//將指定位置的Bean定義資源文件解析爲Spring IOC容器封裝的資源
			//加載單個指定位置的Bean定義資源文件
			Resource resource = resourceLoader.getResource(location);
			//委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
	//重載方法,調用loadBeanDefinitions(String);
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

AbstractRefreshableConfigApplicationContext 的 loadBeanDefinitions(Resource…resources)方
法實際上是調用AbstractBeanDefinitionReader的loadBeanDefinitions()方法。
從對 AbstractBeanDefinitionReader 的 loadBeanDefinitions()方法源碼分析可以看出該方法就做了
兩件事:首先,調用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。其次,真正執行加載功能是其子類 XmlBeanDefinitionReader 的 loadBeanDefinitions()方法。在loadBeanDefinitions()方法中調用了 AbstractApplicationContext的 getResources()方法,跟進去之後發現 getResources()方法其實定義在 ResourcePatternResolver 中,此時,我們有必要來看一下ResourcePatternResolver的全類圖:
在這裏插入圖片描述
從上面可以看到 ResourceLoader 與 ApplicationContext 的繼承關係,可以看出其實際調用的是DefaultResourceLoader中的getSource()方法定位 Resource ,因爲ClassPathXmlApplicationContext 本身就是 DefaultResourceLoader 的實現類,所以此時又回到了ClassPathXmlApplicationContext中來

4.7解析配置文件路徑

XmlBeanDefinitionReader通過調用ClassPathXmlApplicationContext 的父類DefaultResourceLoader的getResource()方法獲取要加載的資源,其源碼如下:

//獲取Resource的具體實現方法
	@Override
	public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");

		for (ProtocolResolver protocolResolver : this.protocolResolvers) {
			Resource resource = protocolResolver.resolve(location, this);
			if (resource != null) {
				return resource;
			}
		}
		//如果是類路徑的方式,那需要使用ClassPathResource 來得到bean 文件的資源對象
		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
		else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			try {
				// 如果是URL方式,使用UrlResource 作爲bean 文件的資源對象
				URL url = new URL(location);
				return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
			}
			catch (MalformedURLException ex) {
				//如果既不是classpath標識,又不是URL標識的Resource定位,則調用
				//容器本身的getResourceByPath方法獲取Resource
				return getResourceByPath(location);
			}
		}
	}

DefaultResourceLoader 提供了 getResourceByPath()方法的實現,就是爲了處理既不是 classpath
標識,又不是URL標識的Resource定位這種情況。

在 ClassPathResource中完成了對整個路徑的解析。這樣,就可以從類路徑上對 IOC 配置文件進行加
載,當然我們可以按照這個邏輯從任何地方加載,在 Spring中我們看到它提供的各種資源抽象,比如
ClassPathResource、URLResource、FileSystemResource等來供我們使用。上面我們看到的是定位
Resource 的一個過程,而這只是加載過程的一部分。例如 FileSystemXmlApplication 容器就重寫了
getResourceByPath()方法:

@Override protected Resource getResourceByPath(String path) {
 if (path.startsWith("/")) { path = path.substring(1); } 
 //這裏使用文件系統資源對象來定義 bean 文件 
 return new FileSystemResource(path); } 
4.8讀取文件配置

繼續回到 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource resource)方法看到代表 bean 文件的資源定義以後的載入過程。

@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		//將讀入的XML資源進行特殊編碼處理
		return loadBeanDefinitions(new EncodedResource(resource));
	}

	
	//這裏是載入XML形式Bean定義資源文件方法
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//將資源文件轉爲InputStream的IO流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//從InputStream中得到XML的解析源
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//這裏是具體的讀取過程
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				//關閉從Resource中得到的IO流
				inputStream.close();
			}
		}
	}

通過源碼分析,載入 Bean配置信息的最後一步是將Bean配置信息轉換爲Document對象,該過程由
documentLoader()方法實現。

	//從特定XML文件中實際載入Bean定義資源的方法
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//將XML文件轉換爲DOM對象,解析過程由documentLoader實現
			Document doc = doLoadDocument(inputSource, resource);
			//這裏是啓動對Bean定義解析的詳細過程,該解析過程會用到Spring的Bean配置規則
			return registerBeanDefinitions(doc, resource);
		}
4.9準備文檔對象

DocumentLoader將Bean配置資源轉換成Document對象的源碼如下:

//使用標準的JAXP將載入的Bean定義資源轉換成document對象
	@Override
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		//創建文件解析器工廠
		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		//創建文檔解析器
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		//解析Spring的Bean定義資源
		return builder.parse(inputSource);
	}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {

		//創建文檔解析工廠
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(namespaceAware);

		//設置解析XML的校驗
		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);
			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
				// Enforce namespace aware for XSD...
				factory.setNamespaceAware(true);
				try {
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					ParserConfigurationException pcex = new ParserConfigurationException(
							"Unable to validate using XSD: Your JAXP provider [" + factory +
							"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
							"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
					pcex.initCause(ex);
					throw pcex;
				}
			}
		}

		return factory;
	}

上面的解析過程是調用 JavaEE 標準的 JAXP 標準進行處理。至此 SpringIOC 容器根據定位的 Bean 配 置信息,將其加載讀入並轉換成爲 Document 對象過程完成。接下來我們要繼續分析 Spring IOC 容器 將載入的 Bean 配置信息轉換爲 Document 對象之後,是如何將其解析爲 SpringIOC 管理的 Bean 對象 並將其註冊到容器中的。

4.1.10分配解析策略

XmlBeanDefinitionReader 類中的 doLoadBeanDefinition()方法是從特定 XML 文件中實際載入 Bean 配置資源的方法,該方法在載入 Bean 配置資源之後將其轉換爲 Document 對象,接下來調用 registerBeanDefinitions() 啓 動 Spring IOC 容 器 對 Bean定義的解析過程 ,registerBeanDefinitions()方法源碼如下:

	//按照Spring的Bean語義要求將Bean定義資源解析並轉換爲容器內部數據結構
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//獲得容器中註冊的Bean數量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//解析過程入口,這裏使用了委派模式,BeanDefinitionDocumentReader只是個接口,
		//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//統計解析的Bean數量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

Bean配置資源的載入解析分爲以下兩個過程:
首先,通過調用 XML解析器將 Bean 配置信息轉換得到 Document對象,但是這些 Document對象
並沒有按照Spring的Bean規則進行解析。這一步是載入的過程
其次,在完成通用的 XML解析之後,按照 Spring Bean 的定義規則對 Document 對象進行解析,其
解 析 過 程 是 在 接 口 BeanDefinitionDocumentReader 的 實 現 類
DefaultBeanDefinitionDocumentReader中實現。

4.1.11將配置載入內存

BeanDefinitionDocumentReader 接 口 通 過 registerBeanDefinitions() 方 法 調 用 其 實 現 類
DefaultBeanDefinitionDocumentReader對Document對象進行解析,解析的代碼如下:

	//根據Spring DTD對Bean的定義規則解析Bean定義Document對象
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		//獲得XML描述符
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//獲得Document的根元素
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}
	//--------------------------------------------------
protected void doRegisterBeanDefinitions(Element root) {
		// Any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.

		//具體的解析過程由BeanDefinitionParserDelegate實現,
		//BeanDefinitionParserDelegate中定義了Spring Bean定義XML文件的各種元素
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		//在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴展性
		preProcessXml(root);
		//從Document的根元素開始進行Bean定義的Document對象
		parseBeanDefinitions(root, this.delegate);
		//在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴展性
		postProcessXml(root);

		this.delegate = parent;
	}

	//創建BeanDefinitionParserDelegate,用於完成真正的解析過程
	protected BeanDefinitionParserDelegate createDelegate(
			XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {

		BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
		//BeanDefinitionParserDelegate初始化Document根元素
		delegate.initDefaults(root, parentDelegate);
		return delegate;
	}

	/**
	 * Parse the elements at the root level in the document:
	 * "import", "alias", "bean".
	 * @param root the DOM root element of the document
	 */
	//使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document對象
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//Bean定義的Document對象使用了Spring默認的XML命名空間
		if (delegate.isDefaultNamespace(root)) {
			//獲取Bean定義的Document對象根元素的所有子節點
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//獲得Document節點是XML元素節點
				if (node instanceof Element) {
					Element ele = (Element) node;
					//Bean定義的Document的元素節點使用的是Spring默認的XML命名空間
					if (delegate.isDefaultNamespace(ele)) {
						//使用Spring的Bean規則解析元素節點
						parseDefaultElement(ele, delegate);
					}
					else {
						//沒有使用Spring默認的XML命名空間,則使用用戶自定義的解//析規則解析元素節點
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//Document的根節點沒有使用Spring默認的命名空間,則使用用戶自定義的
			//解析規則解析Document根節點
			delegate.parseCustomElement(root);
		}
	}


	//使用Spring的Bean規則解析Document元素節點
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//如果元素節點是<Import>導入元素,進行導入解析
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//如果元素節點是<Alias>別名元素,進行別名解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//元素節點既不是導入元素,也不是別名元素,即普通的<Bean>元素,
		//按照Spring的Bean規則解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

	/**
	 * Parse an "import" element and load the bean definitions
	 * from the given resource into the bean factory.
	 */
	//解析<Import>導入元素,從給定的導入路徑加載Bean定義資源到Spring IoC容器中
	protected void importBeanDefinitionResource(Element ele) {
		//獲取給定的導入元素的location屬性
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		//如果導入元素的location屬性值爲空,則沒有導入任何資源,直接返回
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"
		//使用系統變量值解析location屬性值
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

		Set<Resource> actualResources = new LinkedHashSet<>(4);

		// Discover whether the location is an absolute or relative URI
		//標識給定的導入元素的location是否是絕對路徑
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"
			//給定的導入元素的location不是絕對路徑
		}

		// Absolute or relative?
		//給定的導入元素的location是絕對路徑
		if (absoluteLocation) {
			try {
				//使用資源讀入器加載給定路徑的Bean定義資源
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			// No URL -> considering resource location as relative to the current file.
			//給定的導入元素的location是相對路徑
			try {
				int importCount;
				//將給定導入元素的location封裝爲相對路徑資源
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				//封裝的相對路徑資源存在
				if (relativeResource.exists()) {
					//使用資源讀入器加載Bean定義資源
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				//封裝的相對路徑資源不存在
				else {
					//獲取Spring IOC容器資源讀入器的基本路徑
					String baseLocation = getReaderContext().getResource().getURL().toString();
					//根據Spring IOC容器資源讀入器的基本路徑加載給定導入路徑的資源
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
		//在解析完<Import>元素之後,發送容器導入其他資源處理完成事件
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

	/**
	 * Process the given alias element, registering the alias with the registry.
	 */
	//解析<Alias>別名元素,爲Bean向Spring IoC容器註冊別名
	protected void processAliasRegistration(Element ele) {
		//獲取<Alias>別名元素中name的屬性值
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		//獲取<Alias>別名元素中alias的屬性值
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		//<alias>別名元素的name屬性值爲空
		if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		//<alias>別名元素的alias屬性值爲空
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				//向容器的資源讀入器註冊別名
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			//在解析完<Alias>元素之後,發送容器別名處理完成事件
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}

	/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	//解析Bean定義資源Document對象的普通元素
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		// BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類
		//對Document對象中<Bean>元素的解析由BeanDefinitionParserDelegate實現
		// BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				//向Spring IOC容器註冊解析得到的Bean定義,這是Bean定義向IOC容器註冊的入口
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			//在完成向Spring IOC容器註冊解析得到的Bean定義之後,發送註冊事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

通過上述 Spring IOC 容器對載入的 Bean 定義 Document 解析可以看出,我們使用 Spring 時,在
Spring配置文件中可以使用元素來導入 IOC 容器所需要的其他資源,Spring IOC 容器在解
析時會首先將指定導入的資源加載進容器中。使用別名時,SpringIOC 容器首先將別名元素所
定義的別名註冊到容器中。
對於既不是元素,又不是元素的元素,即 Spring配置文件中普通的元素的
解析由BeanDefinitionParserDelegate 類的parseBeanDefinitionElement()方法來實現。這個解析的
過程非常複雜.其實對於xml的解析還是非常的複雜了。因此在上一篇文章中MVC使用中我們使用了properties文件代替了XML文件作爲配置文件。望諒解。

4.1.12 載入<bean>元素

Bean 配置信息中的和元素解析在 DefaultBeanDefinitionDocumentReader 中已
經完成,對 Bean 配置信息中使用最多的元素交由 BeanDefinitionParserDelegate 來解析,
其解析實現的源碼如下:

//解析<Bean>元素的入口
	@Nullable
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
		return parseBeanDefinitionElement(ele, null);
	}

	/**
	 * Parses the supplied {@code <bean>} element. May return {@code null}
	 * if there were errors during parse. Errors are reported to the
	 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
	 */
	//解析Bean定義資源文件中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name和別名屬性
	@Nullable
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		//獲取<Bean>元素中的id屬性值
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//獲取<Bean>元素中的name屬性值
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		//獲取<Bean>元素中的alias屬性值
		List<String> aliases = new ArrayList<>();

		//將<Bean>元素中的所有name屬性值存放到別名中
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		//如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		//檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
		//元素中是否包含子<Bean>元素
		if (containingBean == null) {
			//檢查<Bean>元素所配置的id、name或者別名是否重複
			checkNameUniqueness(beanName, aliases, ele);
		}

		//詳細對<Bean>元素中配置的Bean定義進行解析的地方
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						//如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子元素
						//<Bean>元素,爲解析的Bean生成一個唯一beanName並註冊
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						//如果<Bean>元素中沒有配置id、別名或者name,且包含了子元素
						//<Bean>元素,爲解析的Bean使用別名向IOC容器註冊
						beanName = this.readerContext.generateBeanName(beanDefinition);
						// Register an alias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected for Spring 1.2/2.0 backwards compatibility.
						//爲解析的Bean使用別名註冊時,爲了向後兼容
						//Spring1.2/2.0,給別名添加類名後綴
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}
		//當解析出錯時,返回null
		return null;
	}

只要使用過Spring,對Spring配置文件比較熟悉的人,通過對上述源碼的分析,就會明白我們在Spring
配置文件中元素的中配置的屬性就是通過該方法解析和設置到Bean中去的。
注意:在解析元素過程中沒有創建和實例化 Bean 對象,只是創建了 Bean 對象的定義類
BeanDefinition,將元素中的配置信息設置到 BeanDefinition 中作爲記錄,當依賴注入時才
使用這些記錄信息創建和實例化具體的Bean對象。上面方法中一些對一些配置如元信息(meta)、qualifier 等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的元素時,配置最多的是屬性,因此我們下面繼續分析源碼,瞭解Bean的屬性在解析時是如何設置的。

4.1.13載入<property>元素

BeanDefinitionParserDelegate 在解析調用 parsePropertyElements()方法解析元
素中的屬性子元素,解析源碼如下:

//解析<Bean>元素中的<property>子元素
	public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
		//獲取<Bean>元素中所有的子元素
		NodeList nl = beanEle.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			//如果子元素是<property>子元素,則調用解析<property>子元素方法解析
			if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
				parsePropertyElement((Element) node, bd);
			}
		}
	}
//------------------------------------------
//解析<property>元素
	public void parsePropertyElement(Element ele, BeanDefinition bd) {
		//獲取<property>元素的名字
		String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
		if (!StringUtils.hasLength(propertyName)) {
			error("Tag 'property' must have a 'name' attribute", ele);
			return;
		}
		this.parseState.push(new PropertyEntry(propertyName));
		try {
			//如果一個Bean中已經有同名的property存在,則不進行解析,直接返回。
			//即如果在同一個Bean中配置同名的property,則只有第一個起作用
			if (bd.getPropertyValues().contains(propertyName)) {
				error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
				return;
			}
			//解析獲取property的值
			Object val = parsePropertyValue(ele, bd, propertyName);
			//根據property的名字和值創建property實例
			PropertyValue pv = new PropertyValue(propertyName, val);
			//解析<property>元素中的屬性
			parseMetaElements(ele, pv);
			pv.setSource(extractSource(ele));
			bd.getPropertyValues().addPropertyValue(pv);
		}
		finally {
			this.parseState.pop();
		}
	}
	//--------------------------------
	//解析獲取property值
	@Nullable
	public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
		String elementName = (propertyName != null) ?
						"<property> element for property '" + propertyName + "'" :
						"<constructor-arg> element";

		// Should only have one child element: ref, value, list, etc.
		//獲取<property>的所有子元素,只能是其中一種類型:ref,value,list,etc等
		NodeList nl = ele.getChildNodes();
		Element subElement = null;
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			//子元素不是description和meta屬性
			if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
					!nodeNameEquals(node, META_ELEMENT)) {
				// Child element is what we're looking for.
				if (subElement != null) {
					error(elementName + " must not contain more than one sub-element", ele);
				}
				else {
					//當前<property>元素包含有子元素
					subElement = (Element) node;
				}
			}
		}

		//判斷property的屬性值是ref還是value,不允許既是ref又是value
		boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
		boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
		if ((hasRefAttribute && hasValueAttribute) ||
				((hasRefAttribute || hasValueAttribute) && subElement != null)) {
			error(elementName +
					" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
		}

		//如果屬性是ref,創建一個ref的數據對象RuntimeBeanReference
		//這個對象封裝了ref信息
		if (hasRefAttribute) {
			String refName = ele.getAttribute(REF_ATTRIBUTE);
			if (!StringUtils.hasText(refName)) {
				error(elementName + " contains empty 'ref' attribute", ele);
			}
			//一個指向運行時所依賴對象的引用
			RuntimeBeanReference ref = new RuntimeBeanReference(refName);
			//設置這個ref的數據對象是被當前的property對象所引用
			ref.setSource(extractSource(ele));
			return ref;
		}
		//如果屬性是value,創建一個value的數據對象TypedStringValue
		//這個對象封裝了value信息
		else if (hasValueAttribute) {
			//一個持有String類型值的對象
			TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
			//設置這個value數據對象是被當前的property對象所引用
			valueHolder.setSource(extractSource(ele));
			return valueHolder;
		}
		//如果當前<property>元素還有子元素
		else if (subElement != null) {
			//解析<property>的子元素
			return parsePropertySubElement(subElement, bd);
		}
		else {
			// Neither child element nor "ref" or "value" attribute found.
			//propery屬性中既不是ref,也不是value屬性,解析出錯返回null
			error(elementName + " must specify a ref or value", ele);
			return null;
		}
	}

通過對上述源碼的分析,我們可以瞭解在Spring配置文件中,元素中元素的相關
配置是如何處理的:
1、ref被封裝爲指向依賴對象一個引用。
2、value配置都會封裝成一個字符串類型的對象。
3、ref和value都通過“解析的數據類型屬性值.setSource(extractSource(ele));”方法將屬性值/引用與所引用的屬性關聯起來。
在方法的最後對於元素的子元素通過 parsePropertySubElement ()方法解析,我們繼續分析該方法的源碼,瞭解其解析過程。

4.1.14載入<property>的子元素

在 BeanDefinitionParserDelegate 類中的 parsePropertySubElement()方法對中的子元素解析,源碼如下:

	//解析<property>元素中ref,value或者集合等子元素
	@Nullable
	public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
		//如果<property>沒有使用Spring默認的命名空間,則使用用戶自定義的規則解析內嵌元素
		if (!isDefaultNamespace(ele)) {
			return parseNestedCustomElement(ele, bd);
		}
		//如果子元素是bean,則使用解析<Bean>元素的方法解析
		else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
			BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
			if (nestedBd != null) {
				nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
			}
			return nestedBd;
		}
		//如果子元素是ref,ref中只能有以下3個屬性:bean、local、parent
		else if (nodeNameEquals(ele, REF_ELEMENT)) {
			// A generic reference to any name of any bean.
			//可以不再同一個Spring配置文件中,具體請參考Spring對ref的配置規則
			String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
			boolean toParent = false;
			if (!StringUtils.hasLength(refName)) {
				// A reference to the id of another bean in a parent context.
				//獲取<property>元素中parent屬性值,引用父級容器中的Bean
				refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
				toParent = true;
				if (!StringUtils.hasLength(refName)) {
					error("'bean' or 'parent' is required for <ref> element", ele);
					return null;
				}
			}
			if (!StringUtils.hasText(refName)) {
				error("<ref> element contains empty target attribute", ele);
				return null;
			}
			//創建ref類型數據,指向被引用的對象
			RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
			//設置引用類型值是被當前子元素所引用
			ref.setSource(extractSource(ele));
			return ref;
		}
		//如果子元素是<idref>,使用解析ref元素的方法解析
		else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
			return parseIdRefElement(ele);
		}
		//如果子元素是<value>,使用解析value元素的方法解析
		else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
			return parseValueElement(ele, defaultValueType);
		}
		//如果子元素是null,爲<property>設置一個封裝null值的字符串數據
		else if (nodeNameEquals(ele, NULL_ELEMENT)) {
			// It's a distinguished null value. Let's wrap it in a TypedStringValue
			// object in order to preserve the source location.
			TypedStringValue nullHolder = new TypedStringValue(null);
			nullHolder.setSource(extractSource(ele));
			return nullHolder;
		}
		//如果子元素是<array>,使用解析array集合子元素的方法解析
		else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
			return parseArrayElement(ele, bd);
		}
		//如果子元素是<list>,使用解析list集合子元素的方法解析
		else if (nodeNameEquals(ele, LIST_ELEMENT)) {
			return parseListElement(ele, bd);
		}
		//如果子元素是<set>,使用解析set集合子元素的方法解析
		else if (nodeNameEquals(ele, SET_ELEMENT)) {
			return parseSetElement(ele, bd);
		}
		//如果子元素是<map>,使用解析map集合子元素的方法解析
		else if (nodeNameEquals(ele, MAP_ELEMENT)) {
			return parseMapElement(ele, bd);
		}
		//如果子元素是<props>,使用解析props集合子元素的方法解析
		else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
			return parsePropsElement(ele);
		}
		//既不是ref,又不是value,也不是集合,則子元素配置錯誤,返回null
		else {
			error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
			return null;
		}
	}

通過上述源碼分析,我們明白了在Spring配置文件中,對元素中配置的array、list、set、map、prop 等各種集合子元素的都通過上述方法解析,生成對應的數據對象,比如 ManagedList、ManagedArray、ManagedSet 等,這些Managed類是 Spring對象BeanDefiniton的數據封裝,對集合數據類型的具體解析有各自的解析方法實現,解析方法的命名非常規範,一目瞭然,我們對集合元素的解析方法進行源碼分析,瞭解其實現過程。

4.1.15載入<list>的子元素

在 BeanDefinitionParserDelegate 類中的 parseListElement()方法就是具體實現解析元素中的集合子元素,源碼如下:

//解析<list>集合子元素
	public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
		//獲取<list>元素中的value-type屬性,即獲取集合元素的數據類型
		String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
		//獲取<list>集合元素中的所有子節點
		NodeList nl = collectionEle.getChildNodes();
		//Spring中將List封裝爲ManagedList
		ManagedList<Object> target = new ManagedList<>(nl.getLength());
		target.setSource(extractSource(collectionEle));
		//設置集合目標數據類型
		target.setElementTypeName(defaultElementType);
		target.setMergeEnabled(parseMergeAttribute(collectionEle));
		//具體的<list>元素解析
		parseCollectionElements(nl, target, bd, defaultElementType);
		return target;
	}
//----------------------------------------------------
//具體解析<list>集合元素,<array>、<list>和<set>都使用該方法解析
	protected void parseCollectionElements(
			NodeList elementNodes, Collection<Object> target, @Nullable BeanDefinition bd, String defaultElementType) {
		//遍歷集合所有節點
		for (int i = 0; i < elementNodes.getLength(); i++) {
			Node node = elementNodes.item(i);
			//節點不是description節點
			if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
				//將解析的元素加入集合中,遞歸調用下一個子元素
				target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
			}
		}
	}

對SpringBean配置信息轉換的Document對象中的元素層層解析,SpringIOC現在已經將XML形式定義的Bean配置信息轉換爲SpringIOC所識別的數據結構——BeanDefinition,它是 Bean配置信息中配置的POJO對象在SpringIOC容器中的映射,我們可以通過AbstractBeanDefinition爲入口,看到了IOC容器進行索引,查詢和操作。通過 Spring IOC 容器對 Bean 配置資源的解析後,IOC 容器大致完成了管理 Bean 對象的準備工作,即初始化過程,但是最爲重要的依賴注入還沒有發生,現在在IOC 容器中 BeanDefinition存儲的只是一些靜態信息,接下來需要向容器註冊Bean定義信息才能全部完成IOC 容器的初始化過程

4.1.16 分配註冊策略

我們繼續跟蹤程序的執行順序,接下來我們來分析DefaultBeanDefinitionDocumentReader對Bean定義轉換的Document對象解析的流程中,在其parseDefaultElement()方法中完成對Document對象的解析後得到封裝BeanDefinition的BeanDefinitionHold對象,然後調用BeanDefinitionReaderUtils的registerBeanDefinition()方法向IOC容器註冊解析的 Bean,
BeanDefinitionReaderUtils的註冊的源碼如下:

//將解析的BeanDefinitionHold註冊到容器中
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		//獲取解析的BeanDefinition的名稱
		String beanName = definitionHolder.getBeanName();
		//向IOC容器註冊BeanDefinition
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		//如果解析的BeanDefinition有別名,向容器爲其註冊別名
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

當調用BeanDefinitionReaderUtils向 IOC 容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。

4.1.17 向容器中註冊

DefaultListableBeanFactory 中使用一個 HashMap 的集合對象存放 IOC 容器中註冊解析的
BeanDefinition,向IOC 容器註冊的主要源碼如下:

//存儲註冊信息的BeanDefinition
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//向IOC容器註冊解析的BeanDefiniton
	@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");

		//校驗解析的BeanDefiniton
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);

		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] 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;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		//檢查是否有同名的BeanDefinition已經在IOC容器中註冊
		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			//重置所有已經註冊過的BeanDefinition的緩存
			resetBeanDefinition(beanName);
		}
	}

至此,Bean配置信息中配置的Bean被解析過後,已經註冊到IOC 容器中,被容器管理起來,真正完成了 IOC 容器初始化所做的全部工作。現在 IOC 容器中已經建立了整個 Bean 的配置信息,這些BeanDefinition信息已經可以使用,並且可以被檢索, IOC 容器的作用就是對這些註冊的Bean定義信息進行處理和維護。這些的註冊的 Bean定義信息是IOC 容器控制反轉的基礎,正是有了這些註冊的數據,容器纔可以進行依賴注入。

五、基於註解的IOC容器初始化

關於註解的IOC初始化相比較XML來說,在這也簡單記錄一下

5.1定位Bean的掃描路徑

在 Spring 中管理註解 Bean 定義的容器有兩個 :AnnotationConfigApplicationContext 和
AnnotationConfigWebApplicationContex。這兩個類是專門處理Spring 註解方式配置的容器,直接依賴於註解作爲容器配置信息來源的 IOC 容器。AnnotationConfigWebApplicationContext AnnotationConfigApplicationContext 的 Web版本,兩者的用法以及對註解的處理方式幾乎沒有差別。現在我們以AnnotationConfigApplicationContext爲例看看它的部分源碼:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	//保存一個讀取註解的Bean定義讀取器,並將其設置到容器中
	private final AnnotatedBeanDefinitionReader reader;

	//保存一個掃描指定類路徑中註解Bean定義的掃描器,並將其設置到容器中
	private final ClassPathBeanDefinitionScanner scanner;


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	//默認構造函數,初始化一個空容器,容器不包含任何 Bean 信息,需要在稍後通過調用其register()
	//方法註冊配置類,並調用refresh()方法刷新容器,觸發容器對註解Bean的載入、解析和註冊過程
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	/**
	 * Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
	 * @param beanFactory the DefaultListableBeanFactory instance to use for this context
	 */
	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given annotated classes and automatically refreshing the context.
	 * @param annotatedClasses one or more annotated classes,
	 * e.g. {@link Configuration @Configuration} classes
	 */
	//最常用的構造函數,通過將涉及到的配置類傳遞給該構造函數,以實現將相應配置類中的Bean自動註冊到容器中
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
	}

	/**
	 * Create a new AnnotationConfigApplicationContext, scanning for bean definitions
	 * in the given packages and automatically refreshing the context.
	 * @param basePackages the packages to check for annotated classes
	 */
	//該構造函數會自動掃描以給定的包及其子包下的所有類,並自動識別所有的Spring Bean,將其註冊到容器中
	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}


	/**
	 * {@inheritDoc}
	 * <p>Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader}
	 * and {@link ClassPathBeanDefinitionScanner} members.
	 */
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}

	/**
	 * Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}
	 * and/or {@link ClassPathBeanDefinitionScanner}, if any.
	 * <p>Default is {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
	 * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
	 * and/or {@link #scan(String...)}.
	 * @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
	 * @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
	 */
	//爲容器的註解Bean讀取器和註解Bean掃描器設置Bean名稱產生器
	public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
		this.reader.setBeanNameGenerator(beanNameGenerator);
		this.scanner.setBeanNameGenerator(beanNameGenerator);
		getBeanFactory().registerSingleton(
				AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
	}

	/**
	 * Set the {@link ScopeMetadataResolver} to use for detected bean classes.
	 * <p>The default is an {@link AnnotationScopeMetadataResolver}.
	 * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
	 * and/or {@link #scan(String...)}.
	 */
	//爲容器的註解Bean讀取器和註解Bean掃描器設置作用範圍元信息解析器
	public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
		this.reader.setScopeMetadataResolver(scopeMetadataResolver);
		this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
	}


	//---------------------------------------------------------------------
	// Implementation of AnnotationConfigRegistry
	//---------------------------------------------------------------------

	/**
	 * Register one or more annotated classes to be processed.
	 * <p>Note that {@link #refresh()} must be called in order for the context
	 * to fully process the new classes.
	 * @param annotatedClasses one or more annotated classes,
	 * e.g. {@link Configuration @Configuration} classes
	 * @see #scan(String...)
	 * @see #refresh()
	 */
	//爲容器註冊一個要被處理的註解Bean,新註冊的Bean,必須手動調用容器的
	//refresh()方法刷新容器,觸發容器對新註冊的Bean的處理
	public void register(Class<?>... annotatedClasses) {
		Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
		this.reader.register(annotatedClasses);
	}

	/**
	 * Perform a scan within the specified base packages.
	 * <p>Note that {@link #refresh()} must be called in order for the context
	 * to fully process the new classes.
	 * @param basePackages the packages to check for annotated classes
	 * @see #register(Class...)
	 * @see #refresh()
	 */
	//掃描指定包路徑及其子包下的註解類,爲了使新添加的類被處理,必須手動調用
	//refresh()方法刷新容器
	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		this.scanner.scan(basePackages);
	}
}

通過上面的源碼分析,我們可以看啊到Spring對註解的處理分爲兩種方式:
1)、直接將註解Bean註冊到容器中可以在初始化容器時註冊;也可以在容器創建之後手動調用註冊方法向容器註冊,然後通過手動刷新容器,使得容器對註冊的註解Bean進行處理。
2)、通過掃描指定的包及其子包下的所有類在初始化註解容器時指定要自動掃描的路徑,如果容器創建以後向給定路徑動態添加了註解Bean,則需要手動調用容器掃描的方法,然後手動刷新容器,使得容器對所註冊的Bean進行處理。
接下來,將會對兩種處理方式詳細分析其實現過程。

5.2讀取Annotation元數據

當創建註解處理容器時,如果傳入的初始參數是具體的註解Bean定義類時,註解容器讀取並註冊

5.2.1 AnnotationConfigApplicationContext通過調用註解Bean定義讀取器

AnnotatedBeanDefinitionReader的register()方法向容器註冊指定的註解Bean,註解Bean定義讀取器向容器註冊註解Bean的源碼如下:

//註冊多個註解Bean定義類
	public void register(Class<?>... annotatedClasses) {
		for (Class<?> annotatedClass : annotatedClasses) {
			registerBean(annotatedClass);
		}
	}
//註冊一個註解Bean定義類
	public void registerBean(Class<?> annotatedClass) {
		doRegisterBean(annotatedClass, null, null, null);
	}
//Bean定義讀取器註冊註解Bean定義的入口方法
	@SuppressWarnings("unchecked")
	public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
		doRegisterBean(annotatedClass, null, null, qualifiers);
	}
	//Bean定義讀取器向容器註冊註解Bean定義類
	@SuppressWarnings("unchecked")
	public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
		doRegisterBean(annotatedClass, null, name, qualifiers);
	}
	//Bean定義讀取器向容器註冊註解Bean定義類
	<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

		//根據指定的註解Bean定義類,創建Spring容器中對註解Bean的封裝的數據結構
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);
		//解析註解Bean定義的作用域,若@Scope("prototype"),則Bean爲原型類型;
		//若@Scope("singleton"),則Bean爲單態類型
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		//爲註解Bean定義設置作用域
		abd.setScope(scopeMetadata.getScopeName());
		//爲註解Bean定義生成Bean名稱
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		//處理註解Bean定義中的通用註解
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		//如果在向容器註冊註解Bean定義時,使用了額外的限定符註解,則解析限定符註解。
		//主要是配置的關於autowiring自動依賴注入裝配的限定條件,即@Qualifier註解
		//Spring自動依賴注入裝配默認是按類型裝配,如果使用@Qualifier則按名稱
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				//如果配置了@Primary註解,設置該Bean爲autowiring自動依賴注入裝//配時的首選
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				//如果配置了@Lazy註解,則設置該Bean爲非延遲初始化,如果沒有配置,
				//則該Bean爲預實例化
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				//如果使用了除@Primary和@Lazy以外的其他註解,則爲該Bean添加一
				//個autowiring自動依賴注入裝配限定符,該Bean在進autowiring
				//自動依賴注入裝配時,根據名稱裝配限定符指定的Bean
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}

		//創建一個指定Bean名稱的Bean定義對象,封裝註解Bean定義類數據
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		//根據註解Bean定義類中配置的作用域,創建相應的代理對象
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		//向IOC容器註冊註解Bean類定義對象
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

從上面的源碼我們可以看出,註冊註解Bean定義類的基本步驟:
a、需要使用註解元數據解析器解析註解Bean中關於作用域的配置。
b、使用 AnnotationConfigUtils 的 processCommonDefinitionAnnotations()方法處理註解 Bean 定義類中通用的註解。
c、使用AnnotationConfigUtils的applyScopedProxyMode()方法創建對於作用域的代理對象。
d、通過BeanDefinitionReaderUtils向容器註冊Bean。
下面我們繼續分析這4步的具體實現過程

5.2.2AnnotationScopeMetadataResolver解析作用域元數據

AnnotationScopeMetadataResolver 通過 resolveScopeMetadata()方法解析註解 Bean 定義類的作用域元信息,即判斷註冊的Bean是原生類型(prototype)還是單態(singleton)類型,其源碼如下:

//解析註解Bean定義類中的作用域元信息
	@Override
	public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
		ScopeMetadata metadata = new ScopeMetadata();
		if (definition instanceof AnnotatedBeanDefinition) {
			AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
			//從註解Bean定義類的屬性中查找屬性爲”Scope”的值,即@Scope註解的值
			//annDef.getMetadata().getAnnotationAttributes()方法將Bean
			//中所有的註解和註解的值存放在一個map集合中
			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
					annDef.getMetadata(), this.scopeAnnotationType);
			//將獲取到的@Scope註解的值設置到要返回的對象中
			if (attributes != null) {
				metadata.setScopeName(attributes.getString("value"));
				//獲取@Scope註解中的proxyMode屬性值,在創建代理對象時會用到
				ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
				//如果@Scope的proxyMode屬性爲DEFAULT或者NO
				if (proxyMode == ScopedProxyMode.DEFAULT) {
					//設置proxyMode爲NO
					proxyMode = this.defaultProxyMode;
				}
				//爲返回的元數據設置proxyMode
				metadata.setScopedProxyMode(proxyMode);
			}
		}
		//返回解析的作用域元信息對象
		return metadata;
	}
5.2.3AnnotationConfigUtils處理註解Bean定義類中的通用註解

AnnotationConfigUtils 類的 processCommonDefinitionAnnotations()在向容器註冊 Bean 之前,首先對註解Bean定義類中的通用Spring 註解進行處理,源碼如下:

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
		processCommonDefinitionAnnotations(abd, abd.getMetadata());
	}

	//處理Bean定義中通用註解
	static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
		//如果Bean定義中有@Lazy註解,則將該Bean預實例化屬性設置爲@lazy註解的值
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}

		else if (abd.getMetadata() != metadata) {
			lazy = attributesFor(abd.getMetadata(), Lazy.class);
			if (lazy != null) {
				abd.setLazyInit(lazy.getBoolean("value"));
			}
		}
		//如果Bean定義中有@Primary註解,則爲該Bean設置爲autowiring自動依賴注入裝配的首選對象
		if (metadata.isAnnotated(Primary.class.getName())) {
			abd.setPrimary(true);
		}
		//如果Bean定義中有@ DependsOn註解,則爲該Bean設置所依賴的Bean名稱,
		//容器將確保在實例化該Bean之前首先實例化所依賴的Bean
		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
		if (dependsOn != null) {
			abd.setDependsOn(dependsOn.getStringArray("value"));
		}

		if (abd instanceof AbstractBeanDefinition) {
			AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
			AnnotationAttributes role = attributesFor(metadata, Role.class);
			if (role != null) {
				absBd.setRole(role.getNumber("value").intValue());
			}
			AnnotationAttributes description = attributesFor(metadata, Description.class);
			if (description != null) {
				absBd.setDescription(description.getString("value"));
			}
		}
	}

	//根據作用域爲Bean應用引用的代碼模式
	static BeanDefinitionHolder applyScopedProxyMode(
			ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

		//獲取註解Bean定義類中@Scope註解的proxyMode屬性值
		ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
		//如果配置的@Scope註解的proxyMode屬性值爲NO,則不應用代理模式
		if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
			return definition;
		}
		//獲取配置的@Scope註解的proxyMode屬性值,如果爲TARGET_CLASS
		//則返回true,如果爲INTERFACES,則返回false
		boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
		//爲註冊的Bean創建相應模式的代理對象
		return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
	}
5.2.4AnnotationConfigUtils根據註解Bean定義類中配置的作用域爲其應用相應的代理策略

AnnotationConfigUtils 類的 applyScopedProxyMode()方法根據註解 Bean 定義類中配置的作用域@Scope註解的值,爲Bean定義應用相應的代理模式,主要是在Spring 面向切面編程(AOP)中使用。源碼如下:

//根據作用域爲Bean應用引用的代碼模式
	static BeanDefinitionHolder applyScopedProxyMode(
			ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

		//獲取註解Bean定義類中@Scope註解的proxyMode屬性值
		ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
		//如果配置的@Scope註解的proxyMode屬性值爲NO,則不應用代理模式
		if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
			return definition;
		}
		//獲取配置的@Scope註解的proxyMode屬性值,如果爲TARGET_CLASS
		//則返回true,如果爲INTERFACES,則返回false
		boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
		//爲註冊的Bean創建相應模式的代理對象
		return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
	}
5.2.5 BeanDefinitionReaderUtils向容器註冊Bean

BeanDefinitionReaderUtils 主要是校驗 BeanDefinition 信息,然後將 Bean 添加到容器中一個管理BeanDefinition的HashMap中。

5.3掃描指定包並解析爲BeanDefinition

當創建註解處理容器時,如果傳入的初始參數是註解Bean定義類所在的包時,註解容器將掃描給定的包及其子包,將掃描到的註解Bean定義載入並註冊。

5.3.1ClassPathBeanDefinitionScanner掃描給定的包及其子包

AnnotationConfigApplicationContext 通 過 調 用 類 路 徑 Bean 定 義 掃 描 器ClassPathBeanDefinitionScanner掃描給定包及其子包下的所有類,主要源碼如下:

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
	//創建一個類路徑Bean定義掃描器
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		this(registry, true);
	}
//爲容器創建一個類路徑Bean定義掃描器,並指定是否使用默認的掃描過濾規則。
	//即Spring默認掃描配置:@Component、@Repository、@Service、@Controller
	//註解的Bean,同時也支持JavaEE6的@ManagedBean和JSR-330的@Named註解
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
		this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
	}
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//爲容器設置加載Bean定義的註冊器
		this.registry = registry;

		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		//爲容器設置資源加載器
		setResourceLoader(resourceLoader);
	}
	//調用類路徑Bean定義掃描器入口方法
	public int scan(String... basePackages) {
		//獲取容器中已經註冊的Bean個數
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		//啓動掃描器掃描給定包
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		//註冊註解配置(Annotation config)處理器
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		//返回註冊的Bean個數
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
	//類路徑Bean定義掃描器掃描給定包及其子包
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		//創建一個集合,存放掃描到Bean定義的封裝類
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		//遍歷掃描所有給定的包
		for (String basePackage : basePackages) {
			//調用父類ClassPathScanningCandidateComponentProvider的方法
			//掃描給定類路徑,獲取符合條件的Bean定義
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//遍歷掃描到的Bean
			for (BeanDefinition candidate : candidates) {
				//獲取Bean定義類中@Scope註解的值,即獲取Bean的作用域
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				//爲Bean設置註解配置的作用域
				candidate.setScope(scopeMetadata.getScopeName());
				//爲Bean生成名稱
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				//如果掃描到的Bean不是Spring的註解Bean,則爲Bean設置默認值,
				//設置Bean的自動依賴注入裝配屬性等
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				//如果掃描到的Bean是Spring的註解Bean,則處理其通用的Spring註解
				if (candidate instanceof AnnotatedBeanDefinition) {
					//處理註解Bean中通用的註解,在分析註解Bean定義類讀取器時已經分析過
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//根據Bean名稱檢查指定的Bean是否需要在容器中註冊,或者在容器中衝突
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					//根據註解中配置的作用域,爲Bean應用相應的代理模式
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					//向容器註冊掃描到的Bean
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}

類路徑Bean定義掃描器ClassPathBeanDefinitionScanner主要通過findCandidateComponents()方法調用其父類ClassPathScanningCandidateComponentProvider類來掃描獲取給定包及其子包下的類。

5.3.2 ClassPathScanningCandidateComponentProvider掃描給定包及其子包的類

ClassPathScanningCandidateComponentProvider 類的 findCandidateComponents()方法具體實現掃描給定類路徑包的功能,主要源碼如下:

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
//保存過濾規則要包含的註解,即Spring默認的@Component、@Repository、@Service、
	//@Controller註解的Bean,以及JavaEE6的@ManagedBean和JSR-330的@Named註解
	private final List<TypeFilter> includeFilters = new LinkedList<>();

	//保存過濾規則要排除的註解
	private final List<TypeFilter> excludeFilters = new LinkedList<>();
	//構造方法,該方法在子類ClassPathBeanDefinitionScanner的構造方法中被調用
	public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
		this(useDefaultFilters, new StandardEnvironment());
	}
	public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
		//如果使用Spring默認的過濾規則,則向容器註冊過濾規則
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(null);
	}
	//向容器註冊過濾規則
	@SuppressWarnings("unchecked")
	protected void registerDefaultFilters() {
		//向要包含的過濾規則中添加@Component註解類,注意Spring中@Repository
		//@Service和@Controller都是Component,因爲這些註解都添加了@Component註解
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		//獲取當前類的類加載器
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			//向要包含的過濾規則添加JavaEE6的@ManagedBean註解
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			//向要包含的過濾規則添加@Named註解
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}
	//掃描給定類路徑的包
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}
	private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
		//創建存儲掃描到的類的集合
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			Set<String> types = new HashSet<>();
			for (TypeFilter filter : this.includeFilters) {
				String stereotype = extractStereotype(filter);
				if (stereotype == null) {
					throw new IllegalArgumentException("Failed to extract stereotype from "+ filter);
				}
				types.addAll(index.getCandidateTypes(basePackage, stereotype));
			}
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (String type : types) {
				//爲指定資源獲取元數據讀取器,元信息讀取器通過彙編(ASM)讀//取資源元信息
				MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);
				//如果掃描到的類符合容器配置的過濾規則
				if (isCandidateComponent(metadataReader)) {
					//通過彙編(ASM)讀取資源字節碼中的Bean定義元信息
					AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition(
							metadataReader.getAnnotationMetadata());
					if (isCandidateComponent(sbd)) {
						if (debugEnabled) {
							logger.debug("Using candidate component class from index: " + type);
						}
						candidates.add(sbd);
					}
					else {
						if (debugEnabled) {
							logger.debug("Ignored because not a concrete top-level class: " + type);
						}
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because matching an exclude filter: " + type);
					}
				}
			}
		}
		//判斷元信息讀取器讀取的類是否符合容器定義的註解過濾規則
	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		//如果讀取的類的註解在排除註解過濾規則中,返回false
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		//如果讀取的類的註解在包含的註解的過濾規則中,則返回ture
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		//如果讀取的類的註解既不在排除規則,也不在包含規則中,則返回false
		return false;
	}
}
5.4 註冊註解BeanDefinition

AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 Web 版,
它們對於註解 Bean 的註冊和掃描是基本相同的,但AnnotationConfigWebApplicationContext
對註解Bean 定義的載入稍有不同,AnnotationConfigWebApplicationContext 注入註解Bean定義源碼如下:

//載入註解Bean定義資源
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
		//爲容器設置註解Bean定義讀取器
		AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
		//爲容器設置類路徑Bean定義掃描器
		ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);

		//獲取容器的Bean名稱生成器
		BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
		//爲註解Bean定義讀取器和類路徑掃描器設置Bean名稱生成器
		if (beanNameGenerator != null) {
			reader.setBeanNameGenerator(beanNameGenerator);
			scanner.setBeanNameGenerator(beanNameGenerator);
			beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
		}

		//獲取容器的作用域元信息解析器
		ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
		//爲註解Bean定義讀取器和類路徑掃描器設置作用域元信息解析器
		if (scopeMetadataResolver != null) {
			reader.setScopeMetadataResolver(scopeMetadataResolver);
			scanner.setScopeMetadataResolver(scopeMetadataResolver);
		}

		if (!this.annotatedClasses.isEmpty()) {
			if (logger.isInfoEnabled()) {
				logger.info("Registering annotated classes: [" +
						StringUtils.collectionToCommaDelimitedString(this.annotatedClasses) + "]");
			}
			reader.register(this.annotatedClasses.toArray(new Class<?>[this.annotatedClasses.size()]));
		}

		if (!this.basePackages.isEmpty()) {
			if (logger.isInfoEnabled()) {
				logger.info("Scanning base packages: [" +
						StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
			}
			scanner.scan(this.basePackages.toArray(new String[this.basePackages.size()]));
		}

		//獲取容器定義的Bean定義資源路徑
		String[] configLocations = getConfigLocations();
		//如果定位的Bean定義資源路徑不爲空
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				try {
					//使用當前容器的類加載器加載定位路徑的字節碼類文件
					Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
					if (logger.isInfoEnabled()) {
						logger.info("Successfully resolved class for [" + configLocation + "]");
					}
					reader.register(clazz);
				}
				catch (ClassNotFoundException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("Could not load class for config location [" + configLocation +
								"] - trying package scan. " + ex);
					}
					//如果容器類加載器加載定義路徑的Bean定義資源失敗
					//則啓用容器類路徑掃描器掃描給定路徑包及其子包中的類
					int count = scanner.scan(configLocation);
					if (logger.isInfoEnabled()) {
						if (count == 0) {
							logger.info("No annotated classes found for specified class/package [" + configLocation + "]");
						}
						else {
							logger.info("Found " + count + " annotated classes in package [" + configLocation + "]");
						}
					}
				}
			}
		}
	}

以上就是解析和注入註解配置資源的全過程分析。
我們總結一下以上內容:
1、初始化的入口在容器實現中的refresh()調用來完成。
2、對Bean定義載入IOC容器使用的方法是loadBeanDefinition(),其中的大致過程如下:通過 ResourceLoader來完成資源文件位置的定位,DefaultResourceLoader是默認的實現,同時上下文本身就給出了ResourceLoader的實現,可以從類路徑,文件系統,URL等方式來定爲資源位置。如果是XmlBeanFactory 作爲 IOC容器,那麼需要爲它指定 Bean定義的資源,也 就 是 說 Bean定義文件時通過抽象成 Resource來被IOC容器處理的 ,容器通過BeanDefinitionReader 來完成定義信息的解析和Bean信息的註冊 , 往往使用的是XmlBeanDefinitionReader 來 解 析 Bean 的 XML 定 義 文 件 - 實 際 的 處 理 過 程 是 委 託 給BeanDefinitionParserDelegate 來完成的,從而得到 bean 的定義信息,這些信息在 Spring 中使用BeanDefinition對象來表示-這個名字可以讓我們想到loadBeanDefinition(),registerBeanDefinition()這些相關方法。它們都是爲處理BeanDefinitin服務的,容器解析得到 BeanDefinition 以後,需要把它在 IOC 容器中註冊,這由 IOC 實現BeanDefinitionRegistry 接口來實現。註冊過程就是在IOC 容器內部維護的一個 HashMap 來保存得到的 BeanDefinition 的過程。這個 HashMap 是 IOC 容器持有Bean信息的場所,以後對Bean的操作都是圍繞這個HashMap來實現的。然後我們就可以通過BeanFactory和ApplicationContext來享受到Spring IOC的服務了,在使用IOC容器的時候,我們注意到除了少量粘合代碼,絕大多數以正確IOC 風格編寫的應用程序代碼完全不用關心如何到達工廠,因爲容器將把這些對象與容器管理的其他對象鉤在一起。基本的策略是把工廠放到已
知的地方,最好是放在對預期使用的上下文有意義的地方,以及代碼將實際需要訪問工廠的地方。 Spring本身提供了對聲明式載入web應用程序用法的應用程序上下文,並將其存儲在ServletContext中的框架實現。

對於IOC 整理就以上這些就介紹這麼多,如果發現有補充的地方,我會繼續添加,同時希望各位看過的夥伴們如果發現了問題能夠及時批評指正,在此感謝。
上一篇:深入剖析Spring(三):MVC核心思想
下一篇:深入剖析Spring(五):IOC核心思想(代碼篇)

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