什么是FactoryBean?
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。
回答话术
Spring 中的 FactoryBean
是一个特殊的接口,实现了该接口的 Bean 变为专门用来创建某种特定类型对象的工厂。
它被广泛用于创建一些:
- 无法通过正常的构造函数创建的对象:比较典型的是各种代理,比如 Dubbo 使用
ReferenceBean
创建 RPC 接口的代理对象,Mybatis 使用MapperFactoryBean
来创建 Mapper 接口代理。 - 创建过程比较复杂的对象:比较典型的是
SqlSession
, 比如 JPA 和 Myabtis 都选择通过一个SqlSessionFactoryBean
来创建它。
该接口中定义了三个方法,分别是:
getObject
:用于从工厂中获取一个 Bean 实例。getObjectType
:获取实例的类型。isSingleton
:判断这个 Bean 是否是单例的。
通常情况下,如果 FactoryBean
的 isSingleton
方法返回 true
,则其生产的对象就会是单例的,反之则为多例的。不过,如果 FactoryBean
本身是多例的,那么无论isSingleton
方法是否返回 true
, 其产物也将变为多例的。
我们可以通过 FactoryBean
的 beanName 从 Spring 容器获得其生产的对象,而当需要从 Spring 容器中获取 FactoryBean
本身时,需要在 beanName 前加 &
符号。
问题详解
1. FactoryBean 的 beanName 机制
我们都知道,Spring 容器中所有的 Bean 都有独一无二的 beanName 作为唯一标识,而 FactoryBean
作为一种特殊的 Bean,它的 beanName 代表的除了 FactoryBean
本身外,还代表它的产物 Bean。
在正常情况下,当我们从 Spring 容器通过 FactoryBean
的 beanName 获取 Bean 时,将会得到 FactoryBean
生产的产品,而不是 FactoryBean
本身,如果要获得 FactoryBean
本身,则需要在 beanName 前面加一个 '&' 号。
比如:
@Component("foo")
public static class FooFactoryBean implements FactoryBean<FooFactoryBean.Foo> {
@Override
public Foo getObject() throws Exception {
System.out.println("创建了一个Foo!");
return new Foo();
}
@Override
public Class<?> getObjectType() {
return Foo.class;
}
public static class Foo {}
}
@Component
public class Example {
@Autowired
private ApplicationContext context;
public void run() {
// 通过 'foo' 获得产物 Foo
FooFactoryBean.Foo foo = (FooFactoryBean.Foo)context.getBean("foo");
// 通过 '&foo' 获得 FooFactoryBean
FactoryBean<Foo> fooFactoryBean = (FactoryBean<Foo>)context.getBean("&foo");
}
}
源码中的
AbstractBeanFactory
有一个专门的方法transformedBeanName
用来干这件事情,它在doGetBean
方法中调用。
此外,由于 Spring 只管理 FactoryBean
本身的生命周期而不关心其产物的生命周期, 因此如果你的FactoryBean
的产物本身还是一个 FactoryBean
,那么这个套娃的 FactoryBean
就会被视为一个普通的 Bean,而不会视为一个 FactoryBean
。
比如:
// 在上述基础上再套娃一层
@Component("foo")
public static class FooFactoryBeanFactoryBean implements FactoryBean<FooFactoryBean> {
@Override
public FooFactoryBean getObject() throws Exception {
System.out.println("创建了一个FooFactoryBean!");
return new FooFactoryBean();
}
@Override
public Class<?> getObjectType() {
return FooFactoryBean.class;
}
}
@Component
public class Example {
@Autowired
private ApplicationContext context;
public void run() {
// 通过 'foo' 获得产物 FactoryBean
context.getBean("foo"); // == FooFactoryBean
// 通过 '&foo' 获得 FooFactoryBeanFactoryBean
context.getBean("&foo"); // == FooFactoryBeanFactoryBean
}
}
在上面这个例子中,我们通过 foo
只能够得到下一级产物 FooFactoryBean
,而无法真正的得到我们想要的最下级产物 Foo
。
2. 生命周期
按源码中的注释来说,Spring 只管理 FactoryBean
的生命周期,而不管理其产物的生命周期。
简而言之,FactoryBean
本身像普通的 Bean 一样,会经过各种 BeanPostProcessor
的后处理,并且也支持 InitializingBean
、DisposableBean
以及 Aware
等各种回调接口。
而对于 FactoryBean
的产物来说,它则除了 BeanPostProcessor
的** **postProcessAfterInitialization
外皆不支持。
这一部分内容在源码中的 FactoryBeanRegistrySupport.getObjectFromFactoryBean
方法可以找到答案:
protected Object getObjectFromFactoryBean(
FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// ... ...
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;
// ... ...
}
// AbstractAutowireCapableBeanFactory 实现了这个方法
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
// 调用 BeanPostProcessor 的 postProcessAfterInitialization 方法
return applyBeanPostProcessorsAfterInitialization(object, beanName);
}
作用有限的后处理
实际上,由于缺少前置步骤 postProcessBeforeInitialization
而只有后置步骤 postProcessAfterInitialization
,这导致而很多需要两步协作的后处理器无法正常生效。
例如,用于依赖注入的 AutowiredAnnotationBeanPostProcessor
后处理器无法正常处理它,因此产物 Bean 无法基于 @Autowired
注解进行依赖注入。
不过,还是有一些仅依赖后置步骤的处理器是可以生效的,比如几个基于 AbstractAdvisingBeanPostProcessor
实现后处理器:
AsyncAnnotationBeanPostProcessor
:用于基于@Async
注解实现异步调用。MethodValidationPostProcessor
:基于 JSR303 注解实现参数校验(比如我们属性的@NotNull
)。
举个例子:
@Component("foo")
public static class FooFactoryBean implements FactoryBean<FooFactoryBean.Foo> {
@Override
public Foo getObject() throws Exception {
System.out.println("创建了一个Foo!");
return new Foo();
}
@Override
public Class<?> getObjectType() {
return Foo.class;
}
// 添加 @Async 注解,则 Foo 将会被 Spring 代理
public static class Foo {
private ApplicationContext applicationContext;
@Async
public void test() {}
}
}
@Component
public class Example {
@Autowired
private ApplicationContext context;
public void test() {
// 获取的 foo 被 Spring 代理了
Object foo = context.getBean("foo"); // Test$FooFactoryBean$Foo$$EnhancerBySpringCGLIB$$7887ae4f
}
}
此外顺带一提,
@Async
注解实际上与我们正常的 AOP 走的不是一个后处理器,感兴趣的可以参见:@Async 注解有什么用?它是原理是什么?
3. 作用域
FactoryBean 的作用域可以像正常的 Bean 那样通过 @Scope
配置,而对于它的产物则需要通过内部的 isSingleton 方法来控制。
比如:
@Component("foo") // FactoryBean 默认为单例
public static class FooFactoryBean implements FactoryBean<FooFactoryBean.Foo> {
@Override
public Foo getObject() throws Exception {
System.out.println("创建了一个Foo!");
return new Foo();
}
@Override
public Class<?> getObjectType() {
return Foo.class;
}
@Override // 其产物也默认为单例
boolean isSingleton() {
return true;
}
public static class Foo {}
}
@Component
public class Example {
@Autowired
private ApplicationContext context;
public void run() {
Object foo1 = context.getBean("foo");
Object foo2 = context.getBean("foo");
System.out.printle(foo1 == foo2) // = true
}
}
当然,事无绝对,如果我们把 FactoryBean 设置为多例的,那么即使 isSingleton 返回 true,两者也依然都是多例的。
比如:
@Scope("prototype")
@Component("foo") // FactoryBean 改为单例
public static class FooFactoryBean implements FactoryBean<FooFactoryBean.Foo> {
@Override
public Foo getObject() throws Exception {
System.out.println("创建了一个Foo!");
return new Foo();
}
@Override
public Class<?> getObjectType() {
return Foo.class;
}
@Override // 其产物也默认为单例
boolean isSingleton() {
return true;
}
public static class Foo {}
}
@Component
public class Example {
@Autowired
private ApplicationContext context;
public void run() {
Object foo1 = context.getBean("foo");
Object foo2 = context.getBean("foo");
System.out.printle(foo1 == foo2) // = false
}
}
这一块的逻辑实际上由 FactoryBeanRegistrySupport.getObjectFromFactoryBean
方法控制,在这个方法中,Spring 检查的目标 FactoryBean 是否是单例的,如果是单例的则尝试从缓存中获取,否则直接创建一个新的。
4. 产物的加载时机
我们知道,Spring 中的单例 Bean 会在容器启动的时候通过 ConfigurableListableBeanFactory.preInstantiateSingletons
全部加载,而 FactoryBean
的产物则与普通的 Bean 有所不同:
- 延迟加载:在正常情况下,产物 Bean 并不会随其他正常的 Bean 初始化,只会在被从 Spring 容器中获取的时候才会触发,比如被依赖注入到其他的 Bean 里。
- SmartFactoryBean 中显式指定提前初始化:如果
FactoryBean
又实现了SmartFactoryBean
接口,并重写isEagerInit
方法令其返回 true,那么它将会在它的FactoryBean
初始化后进行初始化。 - 获取产物 Bean 类型导致提前初始化:如果
FactoryBean
的getBeanType
返回null
,那么当调用BeanFactory
的getType
或isTypeMatch
方法时,Spring 就会直接尝试创建一个产物 Bean 出来再获取实际类型,此时就可能会导致提前初始化。
第三点尤其需要注意,当我们调用容器的一些需要获取类型的方法时(比如 ListableBeanFactory.getBeanNamesForType
),都会有提前初始化的风险。
关于容器启动过程,请参见:✅ Spring 容器的启动过程?
5. 循环依赖
FactoryBean
同样会有循环依赖问题,比如 FactoryBean
本身依赖另一个 Bean,而这个 Bean 又依赖从 FactoryBean
获得的产物。
这里 Spring 同样是通过缓存机制解决,不过与普通 Bean 不同,产物 Bean 由于不考虑代理也不考虑初始化问题,因此只使用了 factoryBeanObjectCache
这一级缓存来解决问题。
具体过程请参见:Spring 如何解决循环依赖?
6. 使用场景
FactoryBean
一般适用于两种场景:
- 创建无法通过正常的构造函数创建的对象:比较典型的是各种代理,比如 Dubbo 使用
ReferenceBean
创建 RPC 接口的代理对象,Mybatis 使用MapperFactoryBean
来创建 Mapper 接口代理。 - 创建过程比较复杂的对象:比较典型的是
SqlSession
, 比如 JPA 和 Myabtis 都选择通过一个SqlSessionFactoryBean
来创建它。
这两个优点再概括一下,就是灵活,这里举几个例子:
- 使用
FactoryBean
创建对象可以不需要目标类一定有一个公开的构造方法,我们可以随意的通过任何方式来创建一个对象,比如动态代理,或者直接返回一个静态对象。 - 由于产物不需要被 Spring 管理,所以也不需要担心和其他 Bean 有什么依赖关系,不需要担心与 Spring 容器中的其他 Bean 共享某些组件导致问题。
FactoryBean
本身可以用来作为第三方框架的集成切入点,比如 Mybatis 在创建 Mapper 代理类之前需要先初始化非常多的组件,然而通过FactoryBean
,它不需要将所有的组件交给 Spring 管理,而仅需要把这个过程使用FactoryBean
封装一下就可以与 Spring 直接集成了。
简而言之,FactoryBean
是一个针对特定场景的解决方案,在实际的项目中,当我们比起直接使用构造方法,而更倾向使用工厂方法去创建一个 Bean 的时候,就可以考虑使用 FactoryBean
了。