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是單例還是多例通過反射進行例項化,如果是單例需要新增到快取中 這次的流程不是很複雜,但是其中牽扯到了迴圈引用的問題,在下一篇中,我們來單獨介紹。