【spring源碼系列】之【FactoryBean類型的接口】

1.概述

目前我們知道,spring創建bean有多種方式,比如xml方式創建,比如@Component,@Service,@Controler,@Repository註解創建,比如@Autowired依賴注入創建,後續還有通過springboot方式的配置註解@Configuration與@Bean方式結合創建,這裏不一一介紹,等分析spring boot源碼的時候再做總結。

就spring本身,提供了一種接口方式創建bean,就是本節要討論的通過FactoryBean接口方式創建。

2.實例

FactoryBean接口的實現類FactoryBeanDemo:

package com.wzj.FactoryBean;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Service;

@Service
public class FactoryBeanDemo implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        return new FactoryB();
    }

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

通過FactoryBean實現類,完成自定義類FactoryB的實例化,FactoryB:

package com.wzj.FactoryBean;

import lombok.Data;

@Data
public class FactoryB {
    private String name = "wzj";
}

測試類:

public class TestSpring {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testFactoryBean() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        FactoryB factoryB = (FactoryB)applicationContext.getBean("factoryBeanDemo");
        System.out.println(factoryB);

        FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo)applicationContext.getBean("&factoryBeanDemo");
        System.out.println(factoryBeanDemo);
    }

測試結果:

可以看出,當獲取名稱爲factoryBeanDemo的實例時,得到的是getObject()方法裏創建的FactoryB類型的對象,而獲取加前綴&factoryBeanDemo的實例時,得到的是FactoryBeanDemo本身的實例。

3.源碼

step1: FactoryBean 接口的調用入口在實例化和 IOC/DI 做完後,就會調用 FactoryBean 類型的接口如下圖所示

				// Create bean instance.
				// 創建bean實例
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// FactoryBean的調用入口
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

step2: 如果要獲取到 FactoryBean 類本身,就必須加上&符號,比如 beanFactory.getBean("&beanName") ,如下:

	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不爲空,且以前綴&打頭,直接返回bean本身
		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;
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		if (!(beanInstance instanceof FactoryBean)) {
			return beanInstance;
		}

		
	public static boolean isFactoryDereference(@Nullable String name) {
		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
	}
	String FACTORY_BEAN_PREFIX = "&";

stet3: BeanFactory.getBean("beanName")只能獲取到 getObject()方法返回的實例。getObject 方法返回的實例會有單獨的緩存存儲,跟其他實例不是同一個緩存,對應的緩存是:factoryBeanObjectCache

                // 如果是不是以前綴&打頭,並且是FactoryBean類型的
		Object object = null;
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		else {
			// 從緩存裏拿FactoryBean實例
			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);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			// 緩存沒有的話,
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				// 先從緩存factoryBeanObjectCache取
				Object object = this.factoryBeanObjectCache.get(beanName);
				// 如果緩存爲空,
				if (object == null) {
					// 調用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) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								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;
			}
		}
                ......

小結:具體代碼參考 getSingleton 方法之後 getObjectForBeanInstance

  • 如果bean實例不是 FactoryBean 類型的或者 name 以&開始的則直接返回實例。
  • 如果bean是 FacotyBean 並且不是以&開頭, 會通過方法doGetObjectFromFactoryBean 調用FactoryBean 內部繼承實現的 getObject 方法,並且判斷一級緩存中如果存在該 bean 實例把實例緩存到factoryBeanObjectCache 對應的 map 中,這個是單獨緩存 FactoryBean 類型實例的 map。

4.總結

靈活創建所需實例對象的時候,通過實現FactoryBean接口的getObject方法定義實例化過程。

比如MyBatis提供mybatis-spring項目中的 org.mybatis.spring.SqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	// ...省略其他代碼
	
	public SqlSessionFactory getObject() throws Exception {
	if (this.sqlSessionFactory == null) {
	  afterPropertiesSet();
	}

	return this.sqlSessionFactory;
	}
}

sqlSessionFactory是SqlSessionFactoryBean的一個屬性,它的賦值是在通過回調afterPropertiesSet()方法進行的。 因爲SqlSessionFactoryBean實現了InitializingBean接口,所以在Spring初始化Bean的時候,能回調afterPropertiesSet()方法。

public void afterPropertiesSet() throws Exception {
    // buildSqlSessionFactory()方法會根據mybatis的配置進行初始化。
	this.sqlSessionFactory = buildSqlSessionFactory();
}

在上面的afterPropertiesSet()方法中,buildSqlSessionFactory()方法會根據mybatis的配置,完成客戶所需要的的sessionFactory的初始化。

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