天天看點

Spring 之 IOC 詳解(基于注解方式)

概念

IOC(Inversion of Control)控制反轉:所謂控制反轉,就是應用本身不負責依賴對象的建立和維護,依賴對象的建立及維護是由外部容器負責的。這樣控制權就有應用轉移到了外部容器,控制權的轉移就是控制反轉。

初始化過程

Spring IOC容器的初始化簡單的可以分為三個過程:

  • 第一個過程是Resource資源定位。這個Resouce指的是BeanDefinition的資源定位。這個過程就是容器找資料的過程,就像水桶裝水需要先找到水一樣。
  • 第二個過程是BeanDefinition的載入過程。這個載入過程是把使用者定義好的Bean表示成Ioc容器内部的資料結構,而這個容器内部的資料結構就是BeanDefition。
  • 第三個過程是向IOC容器注冊這些BeanDefinition的過程,這個過程就是将前面的BeanDefition儲存到HashMap中的過程。

注解

從 Spring2.0 以後的版本中,Spring 也引入了基于注解(Annotation)方式的配置,注解(Annotation)是 JDK1.5 中引入的一個新特性,用于簡化 Bean 的配置,可以取代 XML 配置檔案。Spring IOC 容器對于類級别的注解和類内部的注解分以下兩種處理政策:

  • 類級别的注解:如@Component、@Repository、@Controller、@Service 以及 JavaEE6 的@ManagedBean 和@Named 注解,都是添加在類上面的類級别注解,Spring 容器根據注解的過濾規則掃描讀取注解 Bean 定義類,并将其注冊到 Spring IOC 容器中。
  • 類内部的注解:如@Autowire、@Value、@Resource 以及 EJB 和 WebService 相關的注解等,都是添加在類内部的字段或者方法上的類内部注解,SpringIOC 容器通過 Bean 後置注解處理器解析 Bean 内部的注解。下面将根據這兩種處理政策,分别分析 Spring 處理注解相關的源碼。

主要類

核心容器

Spring Bean 的建立是典型的工廠模式,這一系列的 Bean 工廠,也即 IOC 容器為開發者管理對象間的依賴關系提供了很多便利和基礎服務,在 Spring 中有許多的 IOC 容器的實作供使用者選擇和使用,其互相關系如下:

Spring 之 IOC 詳解(基于注解方式)

其中 BeanFactory 作為最頂層的一個接口類,它定義了 IOC 容器的基本功能規範,BeanFactory 有三個重要的子類:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是從類圖中我們可以發現最終的預設實作類是 DefaultListableBeanFactory,它實作了所有的接口。同時 DefaultListableBeanFactory 也是一個注冊器,向工廠注冊 Bean 按照封裝完的資料結構注冊資訊。

資料結構

Spring IOC 容器管理了我們定義的各種 Bean 對象及其互相的關系,Bean 對象在 Spring 實作中是以 BeanDefinition 來描述的,其繼承體系如下:

Spring 之 IOC 詳解(基于注解方式)

資源掃描器

基于注解的方式需要将指定路徑下的所有類都加載進來,在 Spring 中主要是在 ClassPathBeanDefinitionScanner 類中進行實作。類圖如下:

Spring 之 IOC 詳解(基于注解方式)

原理

接下來我們就來根據源碼的方式了解 Spring 注解方式的實作原理。

在 Spring 中 管 理 注 解 Bean 定 義 的 容 器 有 兩 個 : AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContex。這兩個類是專門處理 Spring 注解方式配置的容器,直接依賴于注解作為容器配置資訊來源的 IOC 容器。AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 Web 版本,兩者的用法以及對注解的處理方式幾乎沒有差别。現在我們以 AnnotationConfigApplicationContext 為例,從構造方法為入口進行分析:

public AnnotationConfigApplicationContext(String... basePackages) {
	this();
	scan(basePackages);
	refresh();
}
           

該構造函數會自動掃描已給定的包及其子包下的所有類,并自動識别所有的Spring Bean 将其注冊到容器中。

掃描

我們先來看看 Spring 是如何掃描指定路徑下的 Bean 并注冊到容器中。

public void scan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	this.scanner.scan(basePackages);
}
           

跳轉到ClassPathBeanDefinitionScanner.scanner() 方法上,調用類路徑掃描器入口方法。

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);
}
           

掃描給定包及子包,并将掃描到的 BeanDefinition 進行初始屬性設定後注冊到容器中。源碼如下:

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) {
		//掃描給定類路徑,擷取符合條件的BeanDefinition
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		//周遊掃描到的Bean
		for (BeanDefinition candidate : candidates) {
			//擷取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);
			}
			//如果是Spring的注解Bean,則處理通用的注解
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			//根據Bean名稱檢查指定Bean是否已在容器中注冊過,如果注冊過則判斷兩個BeanDefinition是否相容,不相容則抛出異常
			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;
}
           

掃描給定路徑的包下的所有資源資料,讀取中繼資料封裝成 BeanDefinition 集合傳回。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
	}
	else {
		return scanCandidateComponents(basePackage);
	}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		//将路徑按照一定格式轉換為資源搜尋路徑
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		//從資源搜尋路徑擷取到相關資源資訊
		Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			//資源是否可讀
			if (resource.isReadable()) {
				try {
					//為指定資源擷取中繼資料讀取器,中繼資料讀取器通過ASM讀取資源的元資訊
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
					//判斷元資訊讀取器讀取的類是否符合容器定義的注解過濾規則
					if (isCandidateComponent(metadataReader)) {
						//通過中繼資料讀取器獲得一個通用掃描的BeanDefinition
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}
           

判斷元資訊讀取器讀取的類是否符合容器定義的注解過濾規則

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	//如果讀取的類的注解在排除規則中,傳回false
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}
	//如果讀取的類的注解在包含的注解過濾規則中,則傳回true
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return isConditionMatch(metadataReader);
		}
	}
	//如果讀取的類的注解既不在排除規則中,也不在包含規則中,則傳回false
	return false;
}
           

至此掃描完成,掃描指定路徑下的所有包并封裝成 BeanDefinition 集合。

注冊

先來看下擷取 Bean 作用域的方法,因為作用域對 Spring 建立 Bean 代理對象産生很大的影響。AnnotationScopeMetadataResolver 通過 resolveScopeMetadata() 方法解析注解 Bean 定義類的作用域元資訊,即判斷注冊的 Bean 是原生類型(prototype)還是單态(singleton)類型。源碼如下:

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
	ScopeMetadata metadata = new ScopeMetadata();
	if (definition instanceof AnnotatedBeanDefinition) {
		AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
		//從注解Bean定義類的屬性中查找屬性為Scope的值,即@Scope注解的值
		//annDef.getMetadata()将Bean中所有的注解和注解的值存放在map中
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
				annDef.getMetadata(), this.scopeAnnotationType);
		//将擷取的scope注解的值設定到要傳回的對象中
		if (attributes != null) {
			metadata.setScopeName(attributes.getString("value"));
			//擷取ProxyMode屬性值,在建立代理對象的時候回用
			ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
			//設定proxyMode的屬性值
			if (proxyMode == ScopedProxyMode.DEFAULT) {
				proxyMode = this.defaultProxyMode;
			}
			//為傳回的中繼資料設定proxyMode
			metadata.setScopedProxyMode(proxyMode);
		}
	}
	return metadata;
}
           

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

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	//設定Lazy注解屬性的值
	AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
	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"));
		}
	}

	//判斷是否有@Primary注解,如果有則設定為依賴注入裝配的首選對象
	if (metadata.isAnnotated(Primary.class.getName())) {
		abd.setPrimary(true);
	}
	//如果有@DependsOn注解,則設定需要依賴的Bean名稱
	//容器確定在執行個體化該bean前先執行個體化所依賴的bean
	AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
	if (dependsOn != null) {
		abd.setDependsOn(dependsOn.getStringArray("value"));
	}

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

AnnotationConfigUtils 類的 applyScopedProxyMode()方法根據注解 Bean 定義類中配置的作用域@Scope 注解的值,為 BeanDefinition 應用相應的代理模式,主要是在 Spring 面向切面程式設計(AOP)中使用。

static BeanDefinitionHolder applyScopedProxyMode(
		ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

	//擷取注解Bean定義類中Scope注解的ProxyMode屬性值
	ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
	//如果值為no,則不應用代理模式
	if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
		return definition;
	}
	//如果屬性值為TARGET_CLASS,則傳回true,否則為INTERFACES
	boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
	//為注冊的Bean建立相應模式的代理對象
	return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
           

在上述對 BeanDefinition 完成一系列屬性設定後,接下來才是正式注冊 BeanDefinition 到容器中。通過 BeanDefinitionReaderUtils.registerBeanDefinition() 方法進行注入,源碼如下:

public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	//擷取解析的BeanDefinition的名稱
	String beanName = definitionHolder.getBeanName();
	//向Spring IOC容器注冊BeanDefinition
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// 如果解析的BeanDefinition有别名,向Spring IOC容器注冊别名
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}
           

跳轉到 DefaultListableBeanFactory.registerBeanDefinition() 方法上,從開始對核心容器進行梳理時就說過 DefaultListableBeanFactory 不僅是一個容器也是一個注冊器。注冊源碼如下:

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");

	//檢驗解析的BeanDefinition
	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) {
		//是否允許覆寫上一個相同beanName的beanDefinition
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			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()) {
			//注冊的過程中需要線程同步,以保證資料的一緻性
			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 {
			// 仍處于啟動注冊階段
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}

	//檢查是否已經注冊過同名的BeanDefinition
	if (existingDefinition != null || containsSingleton(beanName)) {
		//重置所有已經注冊過的BeanDefinition的緩存
		resetBeanDefinition(beanName);
	}
}
           

來看下 DefaultListableBeanFactory 重要的兩個屬性:

//存儲注冊資訊的BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//存儲所有已經注冊的BeanName
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
           

至此完成了 IOC 容器注冊的整個過程,将指定路徑下的所有包的 BeanDefinition 注冊到容器中。

重新整理容器

在将指定路徑掃描注冊完成後,會調用 AbstractApplicationContext.refresh 方法完成容器重新整理的過程。

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		// 調用容器準備重新整理的方法,擷取容器的目前時間,同時給容器設定同步辨別
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		// 從資源路徑擷取bean資訊并封裝成beanDefinition注冊到beanFactory的beanDefinitionMap中
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		// beanFactory的預準備工作,對beanFactory配置容器特性,例如類加載器、事件處理器等
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			//beanFactory準備工作完成之後要進行的後置處理工作,留給子類擴充使用
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			// 執行BeanFactory的後置處理器,在BeanFactory标準初始化之後執行的
			// 調用所有注冊的BeanFactoryPostProcessor的postProcessBeanFactory方法
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			// 為BeanFactory注冊Post事件處理器,BeanPostProcessor是Bean的後置處理器,用于監聽容器觸發的事件
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			//初始化MessageSource資訊源,即國際化處理、消息綁定、消息解析
			initMessageSource();

			// Initialize event multicaster for this context.
			//初始化容器事件廣播器,并放入applicationEventMulticaster bean中
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			//留給子類來初始化其他的bean
			onRefresh();

			// Check for listener beans and register them.
			//在所有注冊的bean中查找ApplicationListener,為事件廣播器注冊事件監聽器
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			//初始化所有剩下的非懶加載單執行個體bean
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			//完成重新整理過程,初始化容器的生命周期事件處理器,并釋出容器的生命周期事件
			finishRefresh();
		}

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

			// Destroy already created singletons to avoid dangling resources.
			// 銷毀已經建立的Bean
			destroyBeans();

			// Reset 'active' flag.
			// 取消重新整理操作,重置容器的同步辨別
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			// 重置公共緩存
			resetCommonCaches();
		}
	}
}
           

今天我們主要對 IOC 容器注冊過程進行分析,是以隻分析其中的 obtainFreshBeanFactory 方法。源碼如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	//重新從資源路徑下擷取bean資訊,重新整理beanfactory
	//這裡使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實作調用子類容器的refreshBeanFactory()方法
	refreshBeanFactory();
	//傳回目前實體的beanfactory屬性
	return getBeanFactory();
}
           

跳轉到 AbstractRefreshableApplicationContext.refreshBeanFactory() 方法上。此實作對此上下文的基礎 BeanFactory 進行實際重新整理,關閉前一個 BeanFactory(如果有),并為上下文生命周期的下一階段初始化一個新 BeanFactory。

protected final void refreshBeanFactory() throws BeansException {
	// 判斷是否有BeanFactory,如果已經有容器,則銷毀容器中的Bean,關閉容器
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		//建立DefaultListableBeanFactory,IOC容器
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		//設定序列化id
		beanFactory.setSerializationId(getId());
		//定制beanFactory,設定相關屬性,如設定啟動參數、開啟注解的自動裝配等
		customizeBeanFactory(beanFactory);
		//調用載入Bean定義的方法,此類隻是定義了抽象方法,通過子類容器實作
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}
           

接下來主要看如何對加載 BeanDefinition ,并注冊到容器中。AnnotationConfigWebApplicationContext.loadBeanDefinitions() 方法源碼如下:

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.componentClasses.isEmpty()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Registering component classes: [" +
					StringUtils.collectionToCommaDelimitedString(this.componentClasses) + "]");
		}
		reader.register(ClassUtils.toClassArray(this.componentClasses));
	}

	if (!this.basePackages.isEmpty()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Scanning base packages: [" +
					StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
		}
		scanner.scan(StringUtils.toStringArray(this.basePackages));
	}

	//擷取容器定義的Bean定義資源路徑
	String[] configLocations = getConfigLocations();
	//如果定義的Bean定義資源路徑不為空
	if (configLocations != null) {
		for (String configLocation : configLocations) {
			try {
				//使用目前容器的類加載器加載定位路徑的位元組碼檔案
				Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
				if (logger.isTraceEnabled()) {
					logger.trace("Registering [" + configLocation + "]");
				}
				reader.register(clazz);
			}
			catch (ClassNotFoundException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Could not load class for config location [" + configLocation +
							"] - trying package scan. " + ex);
				}
				//如果容器類加載器加載定義路徑的Bean定義資源失敗,則啟用容器類路徑掃描器掃描給定路徑包及其子包中的類
				int count = scanner.scan(configLocation);
				if (count == 0 && logger.isDebugEnabled()) {
					logger.debug("No component classes found for specified class/package [" + configLocation + "]");
				}
			}
		}
	}
}
           

我們可以看到一樣調用了 ClassPathBeanDefinitionScanner.sacnner() 方法進行掃描并加載到容器中。跟上面掃描流程一緻,就不重複分析。接下來擷取容器中 BeanDefinition 的資源路徑,并加載到容器中。與上面注冊流程一直,也不重複分析。

至此完成了資源路徑下的 BeanDefinition 注冊到 IOC 容器的全流程,真正完成了 IOC 容器初始化所做的全部工作。現在 IOC 容器中已經建立了整個 Bean 的配置資訊,這些BeanDefinition 資訊已經可以使用,并且可以被檢索,IOC 容器的作用就是對這些注冊的 Bean 定義資訊進行處理和維護。這些的注冊的 Bean 定義資訊是 IOC 容器控制反轉的基礎,正是有了這些注冊的資料,容器才可以進行依賴注入。

總結

通過注解的方式向 Spring IOC 容器注入指定路徑資源下的所有 BeanDefinition,與 XML 配置檔案的方式相比。加載資源封裝成 BeanDefinition 流程不同,将 BeanDefinition 注冊到容器中的流程一緻。