天天看點

FactoryBean深入淺出(附源碼講解)

本文章歡迎轉載,但是轉載請标明出處,程式鋒子https://blog.csdn.net/l13591302862/article/details/111866712

平常對

FactoryBean

的認識可能不是很深,這次花了點時間去學習了

FactoryBean

,通過本篇文章進行歸納和總結,希望能夠幫到小夥伴們。本人是小白,能力有限,如果有哪個地方描述錯誤,希望有大佬幫忙指出,先在此謝過。
FactoryBean深入淺出(附源碼講解)

1 簡介

我們先來談談什麼是

FactoryBean

FactoryBean

是一個接口,實作該接口的類可以自定義建立 bean。

FactoryBean

的實作類本質上還是 bean,經曆 bean 的生命周期,但是通過

FactoryBean

建立的 bean 不經曆 bean 的生命周期。

public interface FactoryBean<T> {

    /** 傳回建立的 bean 執行個體 */
	T getObject() throws Exception;

    /** 傳回 bean 執行個體的類型,類型事先未知則傳回 null */
	Class<?> getObjectType();

    /** 傳回 true 表示 singleton ,否則為 prototype */
	default boolean isSingleton() {
		return true;
	}

}
           
ps: 本文使用的

Spring

的版本為

5.1.2

FactoryBean

一般是用來幹什麼的?

FactoryBean

一般用在架構中,用于建立複雜的 bean 執行個體,例如 SpringAOP 中的

ProxyFactoryBean

用于建立 AOP 代理,Dubbo 中的

ReferenceBean

用于建立遠端服務代理。

2 簡單用法

2.1 預設用法(延遲建立 bean)

準備實體類

public class House {
    private String name;
    ...省略getter和setter...
}
           

實作

FactoryBean

接口

@Component("house")
public class HouseFactoryBean implements FactoryBean<House> {

    private static final String HOUSE_NAME = "CoderFengZi";

    @Override
    public House getObject() throws Exception {
        House house = new House();
        house.setName(HOUSE_NAME);
        return house;
    }

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

使用

getBean

方法擷取

bean

執行個體

以下要注意的是,

getBean("beanName")

擷取的是

FactoryBean

建立出來的 bean 執行個體,而

getBean("&beanName")

才是擷取

FactoryBean

本身。

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
        // 擷取FactoryBean建立的bean執行個體
        House house = (House) context.getBean("house");
        // 擷取FactoryBean本身
        FactoryBean<House> factoryBean = (FactoryBean<House>) context.getBean("&house");
        // 運作結果:CoderFengZi
        System.out.println(house.getName());
        // 運作結果:class org.coder.blog.House
        System.out.println(factoryBean.getObjectType());
    }

}
           
ps:使用的

SpringBoot

版本為

2.4.1

2.2 預設是延遲建立 bean 的,如果我們想進行非延遲建立的話怎麼辦?

  • 實作

    FactoryBean

    的子接口

    SmartFactoryBean

    并重寫

    isEagerInit

    方法
  • 實作

    InitializingBean

    接口并且在

    afterPropertiesSet

    方法中建立 bean

a. 實作

SmartFactoryBean

的方式

SmartFactoryBean

FactoryBean

的子接口,可以重寫

isEagerInit

方法,用來進行非延遲建立 bean 執行個體。我們先看下

SmartFactoryBean

有什麼方法?

public interface SmartFactoryBean<T> extends FactoryBean<T> {

    /** 傳回 true 表示 prototype,否則為 singleton */
	default boolean isPrototype() {
		return false;
	}
    
    /** 傳回 true 表示提前建立 bean,否則為延遲建立 */
	default boolean isEagerInit() {
		return false;
	}

}
           

實作

SmartFactoryBean

接口

@Component("eagerHouse")
public class EagerHouseFactoryBean implements SmartFactoryBean<House> {

    private static final String HOUSE_NAME = "CoderFengZi";

    @Override
    public House getObject() throws Exception {
        System.out.println("SmartFactoryBean eager init ...");
        House house = new House();
        house.setName(HOUSE_NAME);
        return house;
    }

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

    /** 設定成非延遲建立 */
    public boolean isEagerInit() {
        return true;
    }
}
           

b. 實作

InitializingBean

的方式

該接口的

afterPropertiesSet

方法會在 bean 執行個體化後調用

AbstractAutowireCapableBeanFactory#invokeInitMethods

方法時被調用,這裡不是重點,不做展開,感興趣的朋友可以觀看Spring中的InitializingBean接口的使用

@Component("house")
public class EagerHouseFactoryBean2 implements FactoryBean<House>, InitializingBean {

    private static final String HOUSE_NAME = "CoderFengZi";
    private volatile House house;

    @Override
    public House getObject() throws Exception {
        if (house == null) {
            house = new House();
            house.setName(HOUSE_NAME);
        }
        return house;
    }

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

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean eager init ...");
        getObject();
    }
}
           

3 源碼分析

3.1 preInstantiateSingletons

DefaultListableBeanFactory#preInstantiateSingletons

以下的代碼為了友善閱讀做了下精簡,

preInstantiateSingletons

方法是在容器

refresh

方法執行尾聲,用來執行個體化所有的非延遲加載的單例 bean,以下代碼主要解釋

SmartFactoryBean

為何能進行非延遲建立。

public void preInstantiateSingletons() throws BeansException {
    ...省略...
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // 建立所有非延遲加載的單例bean
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // 判斷是否是FactoryBean
            if (isFactoryBean(beanName)) {
                // getBean(beanName)擷取的是FactoryBean建立的bean執行個體
                // getBean("&"+beanName)擷取FactoryBean本身
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    final FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    // 判斷是否有代碼運作權限,這裡不做展開
                    // 感興趣的可以看部落格https://www.jianshu.com/p/dcebd60b4c25
                    if (System.getSecurityManager() != null 
                      && factory instanceof SmartFactoryBean) {
                        ...省略...
                    }
                    else {
                        // 預設情況 FactoryBean 延遲建立bean
                        // 但如果是 SmartFactoryBean,而且設定其 eagerInit 值為 true
                        // 那麼就進行提前建立
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                       ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        // 進行提前建立
                        getBean(beanName);
                    }
                }
            }
            else {
                getBean(beanName);
            }
        }
    }
    ...省略...
}
           

3.2 getBean

由于篇幅有限,我們隻研究

getBean

中與

FactoryBean

有關的代碼,簡單看下調用棧,可以發現最終調用的是

getObject

方法。

FactoryBean深入淺出(附源碼講解)

我們從

AbstractBeanFactory#getBean

方法開始調用

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

AbstractBeanFactory#doGetBean

,該方法先從三級緩存中擷取

FactoryBean

的對象,關于三級緩存可以看一文告訴你Spring是如何利用“三級緩存“巧妙解決Bean的循環依賴問題的,然後利用

FactoryBean

執行個體進行下一步操作,如果擷取不到執行個體,那麼就會進行bean的執行個體化,我們現在預設

FactoryBean

執行個體本身已經被執行個體化了。

protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, 
        @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {

    String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    // 此處會從三級緩存中擷取bean執行個體,我們預設FactoryBean之前已經被執行個體化了
    // 關于三級緩存可以看https://blog.csdn.net/f641385712/article/details/92801300
    // 這裡可以擷取到FactoryBean的執行個體
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                ...省略(日志記錄)...
            else {
                ...省略(日志記錄)...
            }
        }
        // 通過FactoryBean執行個體來建立bean
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    
    ...省略...
}
           

AbstractBeanFactory#getObjectForBeanInstance

,這個方法用于擷取 bean 執行個體,先從緩存擷取,如果緩存沒有則建立

protected Object getObjectForBeanInstance(Object beanInstance, String name, 
              String beanName, @Nullable RootBeanDefinition mbd) {
		
    // 下面這段 if 代碼的意思是:
    // 如果不是 FactoryBean,不要使用 & 的名字字首
    // 不然會抛出異常
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        return beanInstance;
    }

    // 非 FactoryBean 直接傳回 bean 執行個體
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd != null) {
        mbd.isFactoryBean = true;
    }
    // 從緩存 factoryBeanObjectCache 的 map 中擷取執行個體
    else {
        object = getCachedObjectForFactoryBean(beanName);
    }
    // 緩存沒有則進行建立
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // 判斷是否是Spring的系統類
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // 擷取bean執行個體
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
           

FactoryBeanRegistrySupport#getObjectFromFactoryBean

,這個方法是擷取

FactoryBean

執行個體的核心方法,主要判斷是進行單例建立還是非單例建立,而且建立後運作一些後置處理邏輯,這些邏輯由

FactoryBeanRegistrySupport

的子類實作。

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) {
                object = doGetObjectFromFactoryBean(factory, beanName);
                // 使用雙重校驗機制,再次嘗試從緩存擷取
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // 如果不是Spring的内部系統類,即!synthetic
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        // 建立單例前進行鎖定,放入singletonsCurrentlyInCreation緩存
                        beforeSingletonCreation(beanName);
                        try {
                            // 可以進行一些後置處理,例如可以進行 AOP 的包裝
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(...);
                        }
                        finally {
                            // 建立單例後解除鎖定,從singletonsCurrentlyInCreation緩存删除
                            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(...);
            }
        }
        return object;
    }
}
           

FactoryBeanRegistrySupport#doGetObjectFromFactoryBean

,該方法最終調用了

FactoryBean

getObject

方法,值得注意的是使用了空對象模式,空對象模式可以看菜鳥教程空對象

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, 
        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 {
            // 調用FactoryBean的getObject方法擷取對象
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(...);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    // 即時擷取的object為空也不傳回null,而傳回NullBean
    // 這裡其實使用了空對象設計模式,可以類比Optional類
    // 對空對象設計模式興趣的可以看下面的位址
    // 菜鳥教程https://www.runoob.com/design-pattern/null-object-pattern.html
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(...);
        }
        object = new NullBean();
    }
    return object;
}
           

4 總結

  • FactoryBean

    的優點是可以自定義建構 bean ,使得建立bean的過程更加靈活。
  • FactoryBean

    本身可以了解成是一種政策模式,我們需要生成什麼樣的 bean,可以通過實作接口來自定義。這就将建立對象的行為獨立出來了,符合前閉後開的原則,是一種非侵入的方式,達到了解耦的目的
  • 我們平常的工作一般可能用不到

    FactoryBean

    ,但是了解有這樣一種機制對我們也是一種提升。
如果有興趣可以微信搜一搜

程式鋒子

,關注本人的微信公衆号
FactoryBean深入淺出(附源碼講解)