文章目錄
- BeanFactory VS FactoryBean
- FactoryBean VS 普通Bean
- 示範
- 源碼
- 使用場景
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CZyQDO1U2MjV2M1UmMxQzMmVDM4gzMjF2NhNjN1UGN28CXxMzLcVDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL1M3Lc9CX6MHc0RHaiojIsJye.png)
BeanFactory VS FactoryBean
首先明确一下,這兩個東西是完全不同的兩個東西 ,不要混淆。
BeanFactory 是Spring Framework的 頂級核心接口 , 沒有這個接口,就沒有Bean的産生。
FactoryBean也是一個接口,是一個特殊的Bean , 實作了FactoryBean 接口的Bean,原來的Bean将會被隐藏,而是由FactoryBean 的getObjecct方法傳回最終的Bean 。 簡單工廠模式 。
如果需要擷取FactoryBean執行個體本身,需要 & 。
可以把FactoryBean了解為4S店,改裝你原來的Bean。
FactoryBean VS 普通Bean
FactoryBean和普通Bean不同,其傳回的對象不是指定類的一個執行個體,而是該FactoryBean的getObject方法所傳回的對象。建立出來的對象是否屬于單例由isSingleton中的傳回決定。
【Bean1】
package com.artisan.factoryBean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class Bean1 {
@PostConstruct
public void init(){
System.out.println("bean1 create ");
}
}
【SpecialBeanFB 實作了 FactoryBean接口 】 用于裝飾Bean
package com.artisan.factoryBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class SpecialBeanFB implements FactoryBean {
@PostConstruct
public void init(){
System.out.println("SpecialBean as Factory Bean create ");
}
@Override
public Object getObject() {
return new Bean2();
}
@Override
public Class<?> getObjectType() {
return Bean2.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
如果SpecialBeanFB這個FactoryBean上的Component注解 增加了 name ,例如
@Component("aaa")
其實是個getObject對象起的名字,而不是本身這個FactoryBean執行個體
當我們需要擷取FactoryBean執行個體本身而不是它所産生的bean,要使用&符号
比如這裡的 id為”specialBeanFB”的FactoryBean :
- 調用getBean(“specialBeanFB”)将傳回FactoryBean産生的bean
- 調用getBean("&specialBeanFB")将傳回FactoryBean它本身的執行個體
【配置類】
package com.artisan.factoryBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.artisan.factoryBean")
public class FBConfig {
}
【測試類】
package com.artisan.factoryBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class FactoryBeanTest {
public static void main(String[] args) throws Exception {
// 執行個體化Spring Bean 容器
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(FBConfig.class);
// 從bean容器中讀取普通的bean
System.out.println(ac.getBean(Bean1.class));
System.out.println(ac.getBean("bean1"));
System.out.println("===========");
// 從bean容器中讀取FactoryBean接口修飾的Bean
System.out.println(ac.getBean(SpecialBeanFB.class).getObject().getClass().getName());
System.out.println(ac.getBean("specialBeanFB"));
System.out.println("===========");
// 通過 & 擷取 FactoryBean本身的bean對象
System.out.println(ac.getBean("&specialBeanFB"));
System.out.println("===========");
// 如果SpecialBeanFB這個FactoryBean上的Component注解 增加了 name ,例如@Component("aaa")
// 其實是個getObject對象起的名字,而不是本身這個FactoryBean執行個體
// System.out.println(ac.getBean("aaa"));
// System.out.println(ac.getBean("&aaa"));
}
}
【測試結果】
在執行個體化Bean的方法中
AbstractApplicationContext # refresh()
---------- finishBeanFactoryInitialization(beanFactory)
----- DefaultListableBeanFactory#preInstantiateSingletons
在Spring容器啟動階段,會調用到refresh()方法,在refresh()中調用了finishBeanFactoryInitialization()方法,最終會調用到beanFactory.preInstantiateSingletons()方法
public void preInstantiateSingletons() throws BeansException {
// 從容器中擷取到所有的beanName
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
// 合并Bean ScannedGenericBeanDefinition AnnotatedGenericBeanDefinition 類型的Bean 等等 都要合并為 RootBeanDefinition 比較複雜,知道即可
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 建立Bean的條件校驗
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 在此處會根據beanName判斷bean是不是一個FactoryBean,實作了FactoryBean接口的bean,會傳回true
if (isFactoryBean(beanName)) {
// 然後通過getBean()方法去擷取或者建立單例對象
// 注意:在此處為beanName拼接了一個字首:FACTORY_BEAN_PREFIX 是一個常量字元串,即:&
// 是以在此時容器啟動階段,對于specialBeanFB,應該是:getBean("&specialBeanFB")
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 下面這一段邏輯,是判斷是否需要在容器啟動階段,就去執行個體化getObject()傳回的對象,即是否調用FactoryBean的getObject()方法
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
}
}
}
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// ...省略其他代碼
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
public void afterPropertiesSet() throws Exception {
// buildSqlSessionFactory()方法會根據mybatis的配置進行初始化。
this.sqlSessionFactory = buildSqlSessionFactory();
}