本文是《再读Spring源码》系列第二篇,一起来探讨下Spring框架常见扩展点FactoryBean,了解它的使用场景和实现原理。
FactoryBean简介
正如名字所言,FactoryBean本质上一个是工厂类,用于构造用户所需的Bean。通过FactoryBean可以隐藏到构造某些Bean的复杂参数以及构建逻辑,暴露更加简化的接口。
FactoryBean的接口方法非常简单:
public interface FactoryBean<T> {
// 获取对象
T getObject() throws Exception;
// 获取对象类型
Class<T> getObjectType();
// 判断是否为单例
boolean isSingleton();
}
FactoryBean使用方法
如下样例,我们可以通过StudentFactoryBean来构建Student实例:
public class Student extends Person {
public Student(DateTime birthday, String name) {
super(birthday, name);
}
}
@Component
public class StudentFactoryBean implements FactoryBean<Student> {
private String name;
private Integer age;
@Override
public Student getObject() throws Exception {
DateTime birthday = new DateTime().minusYears(age);
return new Student(birthday, name);
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
}
这里只是演示下用法,所以StudentFactoryBean的构建逻辑很简单。实际应用中,FactoryBean一般包含大量复杂业务逻辑。
在XML配置中使用FactoryBean
在老版本的spring框架里,常常通过xml配置注入bean。FactoryBean的常见用法是这样的:
<bean class = "a.b.c.StudentFactoryBean" id = "bob">
<property name = "name" value ="Bob"/>
<property name = "age" value ="20"/>
</bean>
<bean class = "a.b.c.Teacher" id = "Tom">
<property name = "student" ref = "bob"/>
</bean>
这里bob实例的类型虽然为StudentFactoryBean,但在实例化的时候,Spring会调用StudentFactoryBean#getObject进行实例化,并返回Student对象。因此bob实例的最终类型是Student。
在注解中使用FactoryBean
在较新版本的Spring中,一般使用注解代替xml配置注入。Factory在注解注入方式的使用上稍有不同。一般如下:
@Bean
public Student getStudent() throws Exception {
return new StudentFactoryBean().setAge(20).setName("Bob").getObject();
}
需要显式地调用getObject方法返回bean对象,并通过 @Bean 注解将类型为Student的bean注册到spring容器中。
FactoryBean在spring中的实现原理
FactoryBean会在spring容器启动过程中进行加载,可以跟踪以下代码:
-> SpringApplicationBuilder#run(String... args)
--> SpringApplicationBuilder#refreshContext(ConfigurableApplicationContext context)
---> SpringApplicationBuilder#refresh(ApplicationContext applicationContext)
----> SpringApplicationBuilder#refresh(ConfigurableApplicationContext applicationContext)
-----> AbstractApplicationContext#refresh()
------> AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
-------> DefaultListableBeanFactory#preInstantiateSingletons()
到最后我们会发现FactoryBean的初始化逻辑:
public void preInstantiateSingletons() throws BeansException {
// ...
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 处理FactoryBean
if (isFactoryBean(beanName)) {
// 实例化该工厂bean(注意工厂bean的名字以&开头)
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
// SmartFactoryBean是FactoryBean的特殊类型;
// 可以通过isEagerInit方法来判断其内部的bean是否需要提前实例化。
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
// 如果需要提前实例化内部实例,则调用getBean进行实例化。(注意内部实例没有&前缀)
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// ......
}
从上边我们可以有以下结论:
- 1.工厂bean可以分为FactoryBean和SmartFactoryBean两种,后者的唯一区别是可以指示是否在初始化工厂Bean之后立即初始化内部实例;
- 2.工厂bean的名字以&为前缀,去掉该前缀则为内部实例的默认名字。例如:”&studentFactoryBean”为工厂bean名,而”studentFactoryBean”是内部实例bean名;
从getBean方法跟踪进去,我们会发现实例化的时候针对工厂bean有特殊的初始化逻辑:
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.
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,则直接返回,不执行以下逻辑
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 工厂bean的内部实例初始化后会有缓存,先尝试通过缓存获取
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 没有内部实例缓存,则尝试初始化
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()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 通过工厂bean获取实例
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;
}
}
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,实例化后的内部实例会放进缓存中,反复复用;而非单例的FactoryBean,每次实例化都会获取新建新的内部实例。
总结
本文详细介绍了FactoryBean的使用方法、实现原理和应用场景。
参考资料
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 duval1024@gmail.com