Skip to main content

什么是FactoryBean?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

回答话术

Spring 中的 FactoryBean 是一个特殊的接口,实现了该接口的 Bean 变为专门用来创建某种特定类型对象的工厂。

它被广泛用于创建一些:

  • 无法通过正常的构造函数创建的对象:比较典型的是各种代理,比如 Dubbo 使用 ReferenceBean 创建 RPC 接口的代理对象,Mybatis 使用 MapperFactoryBean 来创建 Mapper 接口代理。
  • 创建过程比较复杂的对象:比较典型的是 SqlSession, 比如 JPA 和 Myabtis 都选择通过一个 SqlSessionFactoryBean 来创建它。

该接口中定义了三个方法,分别是:

  • getObject :用于从工厂中获取一个 Bean 实例。
  • getObjectType:获取实例的类型。
  • isSingleton:判断这个 Bean 是否是单例的。

通常情况下,如果 FactoryBeanisSingleton 方法返回 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 前面加一个 '&' 号。

image.png

比如:

@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的后处理,并且也支持 InitializingBeanDisposableBean 以及 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 类型导致提前初始化:如果 FactoryBeangetBeanType 返回 null,那么当调用 BeanFactorygetTypeisTypeMatch 方法时,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 了。