一般情況下,Spring通過反射機制利用bean的class屬性指定實現類來實例化bean。在某些情況下,實例化bean過程比較復雜,如果按照傳統的方式,則需要在<bean>中提供大量的配置信息,配置方式的靈活性是受限的,這是采用編碼的方式可能得到一個簡單的方案。Spring為此提供了一個FactoryBean的工廠接口,用戶可以通過實現該接口定制實例化bean的邏輯。個人覺得在spring4.0之后可以用@Bean注解取代FactoryBean。
FactoryBean接口
該接口中定義了三個方法:
//返回由FactoryBean創建的bean實例
T getObject() throws Exception;
//返回FactoryBean創建的bean類型
Class<?> getObjectType();
//返回由FactoryBean創建的bean實例作用域
boolean isSingleton();
當配置文件中的<bean>的class屬性配置的實現類是FactoryBean時,通過getBean()方法返回的不是FactoryBean本身,而是Factory#getObject()方法所返回的對象,相當于FactoryBean#getObject()代理了getBean()方法。
使用案例
Mybatis與spring整合,獲取SqlSessionFactory就是通過FactoryBean,我們整合時的配置如下:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
SqlSessionFactoryBean實現了FactoryBean,getObject()方法返回的就是SqlSessionFactory對象。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
...
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
...
}
原理解析
原理其實已經能夠看出來了,在調用實現FactoryBean接口的bean時,spring會間接調用FactoryBean的getObject()方法,但這代碼在哪實現的呢?
AbstractBeanFactory#getObjectForBeanInstance()方法實現了這個邏輯,這個方法會被getBean()間接調用。
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
//如果對象不是FactoryBean子類或者beanName中有&,直接返回bean
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
//...省略緩存操作
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
...
}
return object;
}
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(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return 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);
}