FactoryBean 和BeanFactory 名字太像了,以至于容易混淆,但是从功能上讲完全不一样, 理解了它们各自的功能就可以完美区分这俩了

1. BeanFactory

Spring IOC 容器,用于管理bean ,具体细节已经在Spring bean 实例化中介绍过,就不再赘述,可以点击去阅读。

总的来说BeanFactory 时Spring 容器,
在Spring的启动过程,FactoryBean 是一个会被BeanFactory 管理的bean , 但是它相比其他普通业务bean,又有一些特殊的功能,比如生产出复杂配置的bean.

接下来详细介绍FactoryBean

2. FactoryBean 是一个接口

  • FactoryBean 接口提供了一种灵活的方式来创建复杂的对象。
  • 实现 FactoryBean 接口的类,可以通过其 getObject 方法来创建并返回实际的对象实例。FactoryBean 可以用于创建单例、原型、代理对象或其他复杂的初始化逻辑。

FactoryBean 接口定义如下:

1
2
3
4
5
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
  • T getObject() throws Exception:这个方法返回由 FactoryBean 创建的对象。这个对象可以是一个普通的 Bean,也可以是一个代理对象或其他复杂对象。
  • Class<?> getObjectType():这个方法返回由 FactoryBean 创建的对象的类型。Spring 使用这个信息来确定 Bean 的类型。
  • boolean isSingleton():这个方法指示由 FactoryBean 创建的对象是否是单例(singleton)。如果返回 true,Spring 容器会缓存该实例。

3. Spring 容器启动过程中的FactoryBean

接下来我们将通过 分析Spring 启动过程中 FactoryBean的实例化以及通过FactoryBean.getObject 的过程来加深对FactoryBean的理解。
这里我们用 Spring AOP XML配置方式原理详解 中的示例代码进行讲解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class UserService {

public void createUser(String username) {

// 模拟业务处理
System.out.println("Creating user: " + username);
}

public void deleteUser(String username) {

// 模拟业务处理
System.out.println("Deleting user: " + username);
}
}

// 定义一个前置增强
public class LoggingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("Before method: " + method.getName());
}
}

// 定义一个后置增强
public class LoggingAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("After method: " + method.getName());
}
}


// 定义一个 环绕增强
public class LoggingMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 方法执行前逻辑
long startTime = System.currentTimeMillis();
System.out.println(invocation.getMethod().getName()+ " 方法开始执行");


// 通过反射执行目标方法
Object result = invocation.proceed();

// 方法执行后逻辑
//System.out.println("After method: " + invocation.getMethod().getName());
long endTime = System.currentTimeMillis();
System.out.println(invocation.getMethod().getName()+ "方法执行时间: " + (endTime - startTime) + "ms");

return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 定义业务类的 Bean -->
<bean id="userService" class="com.example.codingInAction.service.UserService"/>

<!-- 定义 前置增强 -->
<bean id="loggingBeforeAdvice" class="com.example.codingInAction.aop.LoggingBeforeAdvice"/>

<!-- 定义 后置增强 -->
<bean id="loggingAfterAdvice" class="com.example.codingInAction.aop.LoggingAfterAdvice"/>

<!-- 定义 环绕增强 -->
<bean id="loggingMethodInterceptor" class="com.example.codingInAction.aop.LoggingMethodInterceptor"/>

<!-- 使用 ProxyFactoryBean 配置代理对象 -->
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="userService"/>
<!-- 拦截器 -->
<property name="interceptorNames">
<list>
<value>loggingMethodInterceptor</value>
<value>loggingBeforeAdvice</value>
<value>loggingAfterAdvice</value>

</list>
</property>
<!-- 强制使用CGLIB代理 -->
<property name="proxyTargetClass" value="true"/>
</bean>
</beans>

启动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CodingInActionApplication{

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// 获取代理对象
UserService userService = (UserService) context.getBean("userServiceProxy");

// 调用方法,观察 AOP 切面的效果
userService.createUser("john");
userService.deleteUser("john");

}
}

& 前缀的作用

  • 使用 getBean("&beanName") 可以获取 FactoryBean 实例本身,而不是 FactoryBean 创建的对象。
  • 使用 getBean("beanName") 则获取由 FactoryBean 创建的对象。

3.1 FactoryBean 也是一个bean

FactoryBean 也是一个bean, 和普通bean 使用相同的逻辑进行实例化,所以也是被beanFactory 管理的。

如代码所示

1
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

在以上Spring 容器的启动过程中,userServiceProxy作为一个FactoryBean 会通过getBean触发实例化流程。
可以看到getBean传参的时候,beanName 前增加了 & 标识,说明这个getBean 流程需要获取的是userServiceProxy这个个FactoryBean ,而不是由它创建的对象

在实例化 FactoryBean 的过程中,并不需要&标识 , 在getBean 是否,需要判断 是返回FactoryBean 实例 还是Factorybean创建的对象时才需要&标识。

3.1.1 transformedBeanName 方法

进入getBean, 可以看到namet经过了transformedBeanName方法处理, 又把& 去掉了。

1
String beanName = transformedBeanName(name);

这行代码的作用是将传入的 name 转换为实际的 Bean 名称,在 Spring 框架中,Bean 名称可能包含一些特殊的前缀或后缀,用于处理特殊情况或指示某种行为。transformedBeanName 方法用于解析和处理这些前缀或后缀,以获得实际的 Bean 名称。

在这个例子中就是去掉工厂 Bean 前缀 &

& 前缀的作用 告诉程序 ,我是来获取 FactoryBean 本身,而不是获取FactoryBean 创建的对象的。
所以 & 本身不属于bean名称的一部分,需要经过transformedBeanName 方法会去掉这个前缀,以获得实际的 Bean 名称。

对于非FactoryBean 来讲, name 和beanName 都是一样的

3.1.2 getObjectForBeanInstance

FactoryBean只有在实例化后才能通过getObject() 创建其他配置复杂的bean

在看IOC源码的时候,发现即使我们已经创建出来了对象的实例,还是要走一个方法再去处理下,这里就是对FactoryBean的处理,因为它可以产生对象,所以你getBean的时候取到的不是它本身,而是通过它生成的产品。【如果要取它本身,getBean(&+beanName)】 我们先来回忆下IOC源码中那个处理FactoryBean的简略代码:
我们可以看到,无论是直接取单例的bean,还是创建单例、多例、自定义生命周期的bean,都会经过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);这个方法,我们现在就来看看这里到底是做了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

//如果是对FactoryBean的解引用,但bean对象不是FactoryBean,抛出异常
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}

//如果Bean实例不是FactoryBean,或者指定名称是FactoryBean的解引用,也就是普通的bean调用,则直接返回当前的Bean实例
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}

//处理对FactoryBean的调用
Object object = null;
if (mbd == null) {
//从Bean工厂缓存中获取给定名称的实例对象
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean factory = (FactoryBean) beanInstance;
//如果从Bean工厂生产的Bean是单态模式的,则缓存
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
//调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,实现FactoryBean生产Bean对象实例的过程
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Bean工厂生产Bean实例对象
protected Object getObjectFromFactoryBean(FactoryBean factory,
String beanName, boolean shouldPostProcess) {
// Bean工厂是单态模式,并且Bean工厂缓存中存在指定名称的Bean实例对象
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 直接从Bean工厂缓存中获取指定名称的Bean实例对象
Object object = this.factoryBeanObjectCache.get(beanName);
// Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象
if (object == null) {
// 调用Bean工厂的getObject方法生产指定Bean的实例对象
object = doGetObjectFromFactoryBean(factory, beanName,
shouldPostProcess);
// 将生产的实例对象添加到Bean工厂缓存中
this.factoryBeanObjectCache.put(beanName,
(object != null ? object : NULL_OBJECT));
}
return (object != NULL_OBJECT ? object : null);
}
}
// 调用Bean工厂的getObject方法生产指定Bean的实例对象
else {
return doGetObjectFromFactoryBean(factory, beanName,
shouldPostProcess);
}
}

//调用Bean工厂的getObject方法生产指定Bean的实例对象
private Object doGetObjectFromFactoryBean(
final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
//实现PrivilegedExceptionAction接口的匿名内置类
//根据JVM检查权限,然后决定BeanFactory创建实例对象
object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
//调用BeanFactory接口实现类的创建对象方法
return factory.getObject();
}
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//调用BeanFactory接口实现类的创建对象方法
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);
}
//创建出来的实例对象为null,或者因为单态对象正在创建而返回null
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
//为创建出来的Bean实例对象添加BeanPostProcessor后置处理器
if (object != null && shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
}
}
return object;
}

经过getObjectForBeanInstance 处理,最终返回了userServiceProxy这个FactoryBean 本身

3.2 获取FactoryBean 创建的对象

FactoryBean 本身已经实例化完成了,现在可以获取由它创建的对象了

1
2
 // 获取代理对象
UserService userService = (UserService) context.getBean("userServiceProxy");

由于userServiceProxy 是FactoryBean, 现在执行getBean("beanName") 说明我们要获取由它创建的对象了。下面跟着代码进行简单分析。

3.2.1 transformedBeanName 方法

进入源码,再次来到getBean 流程,可以看到,经过transformedBeanName处理后,name 和beanName 保持一致。

3.2.2 getObjectForBeanInstance

此时getSingleton,一级缓存中已经有了IOC 容器启动过程的中创建的实例, 直接获取即可。再次进入getObjectForBeanInstance 方法

3.2.3 FactoryBean.getObject

在getObjectForBeanInstance 方法中,逐渐深入最终会进入到ProxyFactoryBean 重写的getObject 方法(getObject 是接口FactoryBean 中定义的方法还记得吗)

在getObject 方法中,可以获取userServiceProxy 创建的对象,即一个代理对象

4. FactoryBean 的应用场景

FactoryBean 可以用于各种高级应用场景,以下是一些典型的使用场景:

4.1 创建代理对象

FactoryBean 常用于创建 AOP 代理对象。例如,Spring AOP 内部大量使用 FactoryBean 来创建代理对象。

在上面的动态代理Demo中,我们就展示了一个通过FactoryBean创建代理对象的复杂例子。

4.2 创建复杂的 Bean 实例

在某些情况下,创建一个 Bean 可能涉及复杂的初始化逻辑。通过 FactoryBean,我们可以将这些逻辑封装在 getObject 方法中,从而简化配置。

很多开源项目在集成Spring 时都使用到FactoryBean,比如 MyBatis3 提供 mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean:

1
2
3
4
5
6
<!-- spring和MyBatis整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>

1
2
3
4
5
6
7
8
9
10
11
12
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
...
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}

return this.sqlSessionFactory;
}
...
}