1. 程式人生 > >Spring之bean例項的建立

Spring之bean例項的建立

在上一篇部落格中,我們已經對bean的載入跟著原始碼完整的走了一遍,程式碼讀起來很輕鬆,主要還是Spring的程式設計風格,它把每段程式碼分割成一個一個的小邏輯,使得每個邏輯處理起來都不是很負責,非常有利於我們學習。接下來,我們繼續對bean例項的建立進行閱讀。 Spring啟動之後,如果我們不主動從裡面獲取bean的例項,Spring是不會主動建立例項的,所以例項的建立流程是從下面這段程式碼開始的

Person person = (Person) beanFactory.getBean("person");

雖然只有簡單的一句,但是他後面的邏輯確是很複雜的,我們開始。


    public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

同樣符合我們發現的規律,幹活的永遠都是do開頭的方法,這就類似於被戴了帽子吧!

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		//首先將使用者傳入的名字變成在IOC儲存的beanName
		final String beanName = transformedBeanName(name);
		Object bean;

		// 先按照使用者要到的bean是單例來查詢
		Object sharedInstance = getSingleton(beanName);
		//如果能從IOC容器的快取中查到,那麼這個bean一定是單例並且已經被載入過了
		if (sharedInstance != null && args == null) {
			...
			//由於使用者獲得的bean可能是繼承了FactroyBean的bean,所以還要進行一些驗證和處理
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 我們假設在迴圈引用當中
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			...
			try {
				//獲得一個合併的beanDefinition,其實就是複製了一個beanDefinition並且對其中缺少的屬性進行填充
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				//確保初始化當前bean所依賴的bean都初始化了
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						getBean(dep);
					}
				}

				// 建立單例
				if (mbd.isSingleton()) {
					//建立單例的具體方法
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//建立一個單例
				else if (mbd.isPrototype()) {
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
							//由於使用者獲得的bean可能是繼承了FactroyBean的bean,所以還要進行一些驗證和處理
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
								//由於使用者獲得的bean可能是繼承了FactroyBean的bean,所以還要進行一些驗證和處理
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		...
		return (T) bean;
	}

這個方法先假設要獲取的bean是一個單例,如果從IOC容器中得到了例項,說明他是一個單例,呼叫getObjectForBeanInstance直接返回,如果沒有獲得到,建立一個MergedBeanDefinition根據他所定義的是單例還是多例進行bean的例項,在這個方法中還牽扯到了迴圈引用的問題,在這篇部落格中我們就不細講了,之後要單獨寫一篇部落格介紹它,接下來我們先看一下怎麼從IOC容器中獲得單例:

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//從IOC容器的快取中查詢
		Object singletonObject = this.singletonObjects.get(beanName);
		//判斷對應的單例物件是否在建立中,當單例物件沒有被初始化完全(例如A定義的建構函式依賴了B物件,得先去建立B物件,或者在populatebean過程中依賴了B物件,得先去建立B物件,此時A處於建立中)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//在多執行緒環境中,對一級快取上鎖
			synchronized (this.singletonObjects) {
				//從二級快取中獲取物件
				singletonObject = this.earlySingletonObjects.get(beanName);
				//如果沒有獲得到,可能要獲得的bean在三級快取中
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//從三級快取中獲得到了物件,並且提升它到二級快取中
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

這個方法中先是從一級快取中獲取例項,獲取不到,再從二級快取中獲取,最後再從三級快取中獲取,Spring中設定三級快取主要是為了解決單例的迴圈引用,在這裡就不細講了。 獲取到了例項最終都要執行這個方法,那麼我們就先來看看他是做什麼的

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
		...
		// 如果instance不是FactoryBean例項,或者想要獲取的就是FactoryBean例項,那麼直接返回就好
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			//獲取快取的,通過FactoryBean暴露出來的物件
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// 需要從FactoryBean中獲取例項
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			//如果物件是單例物件,則快取從FactoryBean獲得的物件。
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

如果我們建立的是一個FactoryBean,而使用者想要bean例項,我們來看Spring是怎麼處理的

protected Object getCachedObjectForFactoryBean(String beanName) {
		return this.factoryBeanObjectCache.get(beanName);
	}

會先嚐試從快取中獲取,這個快取中專門快取了有FactoryBean建立的物件,找到後就直接返回了,沒有還要繼續處理:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		//如果這個factoryBean是單例的,並且已經有了他的快取
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				//從快取中直接獲取factoryBean建立的物件
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					//沒有獲取到,說明快取中沒有,從factory中直接getObject
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							try {
								//這個方法是交給使用者用來定製處理的
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
						}	
						//將獲取的物件放到快取中
						this.factoryBeanObjectCache.put(beanName, object);
					}
				}
				return object;
			}
		}
		else {
			//factoryBean是多例的,每次都要獲取
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

Spring再次嘗試從快取中獲取,獲取不到說明快取中真的沒有了,那麼就要從factoryBean中獲取,獲取完之後需要將這個物件放到快取中,好了,到這使用者就得到了他想要的bean例項了。接著我們看一下這個例項到底是怎麼創建出來的,我們只看單例是怎麼建立的,多例無非就是每次建立完後都不快取。

if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							...
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

這裡是建立單例的入口,從這個地方開始分析,我們會發現這段程式碼裡有個回撥函式,裡賣弄有一個createBean方法,看來他是建立bean例項的核心了,那我們就先來看他:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		...
		RootBeanDefinition mbdToUse = mbd;

		// 獲得我們要例項的bean的class物件
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
		...
		}

		try {
			// 讓beanpostprocessor有機會返回一個代理而不是目標bean例項。
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			...
		}

		try {
			//建立一個例項
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isDebugEnabled()) {
				logger.debug("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException ex) {
			...
		}
	}

同樣,幹活的還是那種戴帽子的方法doCreateBean,但是由於裡面牽扯到了迴圈引用的問題,下一篇文章在講,我們只需要知道在這個方法中,例項化了bean,並且進行了屬性的填充。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		//鎖住快取
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				...
				//建立物件之間先做一些檢查
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					//執行回撥函式,獲得建立的例項
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					...
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					將建立的例項放到快取中
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

在這個方法中得到了建立的單例例項並且把它放到了快取中。執行到這,最簡單的例項化一個bean已經完成了,剩下的就是檢查時候使使用者所需要的型別。我們看一下例項化的流程: 一、指定要獲取的例項的id 二、首先將使用者傳入的名字變成在IOC儲存的beanName 三、先在快取中查詢,如果找到了說明是單例,接著進行根據使用者的要求獲取factoryBean或者是普通bean返回就好了,快取中沒有 四、快取中沒有,要根據使用者要例項的bean是單例還是多例通過反射進行例項化,如果是單例需要新增到快取中 這次的流程不是很複雜,但是其中牽扯到了迴圈引用的問題,在下一篇中,我們來單獨介紹。