Spring 通過BeanInstance獲取對象

Spring Bean 的實例化_02

你的贊是我最大的動力,期待與你共同進步

  在上篇文章中提到 Object sharedInstance = getSingleton(beanName); 這一行代碼,在容器初始化的時候返回的對象爲null。 但是,今天這篇文章中,我要先來處理 sharedInstance 不爲null的情況,看看Spring做了什麼樣的處理。對應下述流程圖中的方法: doGetBean_01

getObjectForBeanInstance

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    /**
     * 如果指定的name是工廠相關(以 & 爲前綴)
     * 且beanInstance又不是 FactoryBean類型 則驗證不通過
     */
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
    }

    /**
     * 有了bean的實例,這個實例通常是 bean 或者是 FactoryBean
     * 如果是 FactoryBean 使用它創建實例,如果用戶想要直接獲取工廠實例
     * 而不是工廠的 getObject() 方法對應的實例,那麼傳入的name 應該加前綴 &
     */
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    // 加載 FactoryBean
    Object object = null;
    if (mbd == null) {
        // 嘗試從緩存中加載bean
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // beanInstance 一定是 FactoryBean
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            /**
             * 將存儲Xml配置文件的 GenericBeanDefinition 轉換爲 RootBeanDefinition
             * 如果指定BeanName是子 Bean 的話同時會合並父類的相關屬性
             */
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // 是否用戶定義的,而不是應用程序本身定義的
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
複製代碼

  該方法是個高頻使用的方法,無論是存緩存中獲得bean 還是根據不同的 scope 策略加載bean。總之得到bean的實例之後要做的第一步就是 調用這個方法來檢測一下正確性,其實就是檢測當前的bean是否是FactoryBean類型的bean,如果是,那麼需要調用該bean對應的FactoryBean實例中的getObject() 作爲返回值。

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 如果是單例模式
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                /** 通過FactoryBean獲取對象*/
                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) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            /**
                             * 調用 ObjectFactory的後置處理器
                             * AbstractAutowireCapableBeanFactory#postProcessObjectFromFactoryBean()
                             * 儘可能保證所有的bean初始化之後都會調用註冊的
                             * BeanPostProcessor 的 postProcessAfterInitialization 方法
                             */
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        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;
    }
}
複製代碼

通過FactoryBean 獲取對象

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
        throws BeanCreationException {

    Object object;
    try {
        // 這裏是權限驗證,不重要
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 直接調用 getObject()方法
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    return object;
}
複製代碼

  上述代碼中最重要的一行就是 object = factory.getObject();。可以看到,這裏的 factory 就是我們傳入的 beanInstance。 在 getObjectForBeanInstance 中做了轉換FactoryBean<?> factory = (FactoryBean<?>) beanInstance; 。 回到最初的起點,getBean(beanName) 通過 beanName 獲取對象,最終得到的是調用 FactoryBeangetObject() 方法返回的對象。

何爲 FactoryBean

  FactoryBean是一個工廠Bean,可以生成某一個類型Bean實例。通過使用 FactoryBean 我們可以自定義 Bean 的創建。在Spring中 FactoryBean 支持泛型。當在IOC容器中的Bean實現了FactoryBean後,通過getBean(String BeanName)獲取到的Bean對象並不是FactoryBean 的實現類對象,而是這個實現類中的getObject()方法返回的對象。要想獲取FactoryBean的實現類,就要getBean(&BeanName),在BeanName之前加上"&"。

public interface FactoryBean<T> {

 /**
  * 返回由FactoryBean創建的bean實例,如果 isSingleton返回true,則該實例會放置到spring容器中單實例緩存池中
  */
 @Nullable
 T getObject() throws Exception;

 /**
  * 返回FactoryBean 創建的實例Bean的類型
  */
 @Nullable
 Class<?> getObjectType();

 /**
  * 用來判斷有FactoryBean創建的是bean的作用域
  */
 default boolean isSingleton() {
  return true;
 }
}
複製代碼

  上述的 FactoryBean<T>接口, 就是Spring中定義好的接口。從上述接口中的方法我們可以看出:FactoryBean中定義了一個Spring Bean的很重要的三個特性:是否單例、Bean類型、Bean實例。下面我們就簡單的使用一下 FactoryBean

FactoryBean接口的實現類

@Component
public class FactoryBeanTest implements FactoryBean<User> {
 @Override
 public User getObject() throws Exception {
  return new User();
 }

 @Override
 public Class<?> getObjectType() {
  return User.class;
 }

 @Override
 public boolean isSingleton() {
  return true;
 }
}
複製代碼

  在上面的重寫方法getObject() 方法中,我採用了 new 的方式來控制 Bean 的創建過程。

對象類

public class User {

 private String name;

 private int age;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }
}
複製代碼

測試方法

public class FactoryBeanTest {
 public static void main(String[] args) {
  ApplicationContext ann = new AnnotationConfigApplicationContext(MyConfig.class);
  // 方式一
  FactoryBeanTest beanTest = ann.getBean(FactoryBeanTest.class);
  System.out.println(beanTest);
  // 方式二
  Object user = ann.getBean("factoryBeanTest");
  System.out.println(user);
  // 方式三
  Object user2 = ann.getBean("&factoryBeanTest");
  System.out.println(user2);
 }
}
複製代碼

  程序運行結果如下: FactoryBean測試結果   從結果我們可以看出,通過類型 FactoryBeanTest.class 與通過使用 "&"+beanName 的方式(&factoryBeanTest);獲取到的對象 是同一個,這個對象是就是 FactoryBean的實現類。而通過beanName 獲取到的對象,就是通過 FactoryBeangetObject() 方法返回的對象。


 

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