getBean源码实战(四)------FactoryBean
代码仓库 :Gitee 仓库链接
本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。
1. FactoryBean 简介
1.1 什么是 FactoryBean
FactoryBean 是 Spring 提供的一个特殊接口,用于创建复杂的 Bean。当我们需要创建的对象比较复杂,或者需要额外的初始化逻辑时,可以使用 FactoryBean。
核心特点:
getObject()方法返回实际要创建的 Bean 实例getObjectType()返回 Bean 的类型isSingleton()返回是否是单例
1.2 示例 Bean:ConnectionFactoryBean
Bean 类定义:
ConnectionFactoryBean:
1:92:spring-source/code/spring-basic/src/main/java/com/example/factorybean/ConnectionFactoryBean.java
package com.example.factorybean;
import org.springframework.beans.factory.FactoryBean;
/**
* FactoryBean 示例:用于创建数据库连接对象
*
* FactoryBean 是 Spring 提供的一个特殊接口,用于创建复杂的 Bean。
* 当我们需要创建的对象比较复杂,或者需要额外的初始化逻辑时,可以使用 FactoryBean。
*
* 核心特点:
* 1. getObject() 方法返回实际要创建的 Bean 实例
* 2. getObjectType() 返回 Bean 的类型
* 3. isSingleton() 返回是否是单例
*/
public class ConnectionFactoryBean implements FactoryBean<Connection> {
private String url;
private String username;
private String password;
private String driverClassName;
/**
* 返回 FactoryBean 创建的对象实例
* 这是 FactoryBean 的核心方法,Spring 容器会调用这个方法来获取实际的 Bean
*/
@Override
public Connection getObject() throws Exception {
System.out.println("ConnectionFactoryBean.getObject() 被调用,创建 Connection 对象");
// 创建 Connection 对象(这里简化处理,实际应该使用真实的数据库驱动)
Connection connection = new Connection();
connection.setUrl(url);
connection.setUsername(username);
connection.setPassword(password);
connection.setDriverClassName(driverClassName);
connection.connect();
return connection;
}
/**
* 返回 FactoryBean 创建的对象类型
* Spring 容器使用这个方法来确定 Bean 的类型
*/
@Override
public Class<?> getObjectType() {
return Connection.class;
}
/**
* 返回 FactoryBean 创建的对象是否是单例
* true 表示单例,false 表示每次获取都创建新实例
*/
@Override
public boolean isSingleton() {
return true; // 连接对象通常是单例
}
// Getter 和 Setter 方法
// ... (省略)
}
Connection:
1:85:spring-source/code/spring-basic/src/main/java/com/example/factorybean/Connection.java
package com.example.factorybean;
/**
* 数据库连接类,由 FactoryBean 创建
*/
public class Connection {
private String url;
private String username;
private String password;
private String driverClassName;
private boolean connected = false;
public Connection() {
System.out.println("Connection 构造函数被调用");
}
/**
* 连接数据库
*/
public void connect() {
System.out.println("正在连接数据库...");
System.out.println("URL: " + url);
System.out.println("Username: " + username);
// 实际应用中这里会执行真实的数据库连接逻辑
this.connected = true;
System.out.println("数据库连接成功!");
}
// ... (其他方法省略)
}
配置文件:
1:26:spring-source/code/spring-basic/src/main/resources/applicationContext-factorybean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
FactoryBean 配置示例
核心要点:
1. 配置的是 FactoryBean 本身(ConnectionFactoryBean),而不是目标对象(Connection)
2. Spring 容器会识别这是一个 FactoryBean,调用其 getObject() 方法获取实际对象
3. 通过 id="connection" 获取时,得到的是 Connection 对象,而不是 ConnectionFactoryBean
4. 如果需要获取 FactoryBean 本身,需要在 bean id 前加 "&" 符号,如 "&connection"
-->
<!-- 配置 FactoryBean -->
<bean id="connection" class="com.example.factorybean.ConnectionFactoryBean">
<property name="url" value="jdbc:mysql://localhost:3306/test_db"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
</beans>
1.3 代码仓库位置
- ConnectionFactoryBean 类 :
code/spring-basic/src/main/java/com/example/factorybean/ConnectionFactoryBean.java - Connection 类 :
code/spring-basic/src/main/java/com/example/factorybean/Connection.java - 配置文件 :
code/spring-basic/src/main/resources/applicationContext-factorybean.xml - 测试类 :
code/spring-basic/src/test/java/com/example/factorybean/FactoryBeanTest.java
1.4 两种获取方式
方式一:获取 FactoryBean 创建的对象
java
Connection connection = factory.getBean("connection", Connection.class);
方式二:获取 FactoryBean 本身
java
FactoryBean<?> factoryBean = factory.getBean("&connection", FactoryBean.class);
核心区别:
- 方式一:通过
"connection"获取,得到的是Connection对象(FactoryBean 创建的目标对象) - 方式二:通过
"&connection"获取,得到的是ConnectionFactoryBean对象(FactoryBean 本身)
2. 深入源码:两种获取方式的区别
2.1 核心方法:transformedBeanName
源码位置 :AbstractBeanFactory.transformedBeanName()
java
// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
return name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
// BeanFactory.java
String FACTORY_BEAN_PREFIX = "&";
处理逻辑:
-
检查是否有
&前缀:- 如果
name以"&"开头,说明要获取 FactoryBean 本身 - 如果
name不以"&"开头,说明要获取 FactoryBean 创建的对象
- 如果
-
去除
&前缀:- 如果以
"&"开头,调用substring(1)去除前缀,得到实际的 Bean 名称 - 例如:
"&connection"→"connection"
- 如果以
-
别名转换:
- 调用
canonicalName()方法,将别名转换为实际的 Bean 名称(支持链式别名)
- 调用
关键点:
transformedBeanName()方法会去除&前缀,但不会丢失这个信息- 原始的名称(包含
&前缀)会传递给getObjectForBeanInstance()方法 getObjectForBeanInstance()方法会根据原始名称是否包含&前缀来决定返回什么
2.2 方式一:获取 FactoryBean 创建的对象
调用代码:
java
Connection connection = factory.getBean("connection", Connection.class);
执行流程:
步骤 1:进入 doGetBean 方法
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 步骤1:翻译Bean名称
final String beanName = transformedBeanName(name); // "connection"
// ... 后续步骤
}
处理过程:
- 传入的
name是"connection"(没有&前缀) transformedBeanName("connection")返回"connection"beanName为"connection"
步骤 2:从缓存获取或创建 Bean
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name); // "connection"
// 步骤2:从缓存获取或创建Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果缓存中有,调用getObjectForBeanInstance处理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
return bean;
}
// 如果缓存中没有,进入创建流程
// ... (创建Bean的逻辑,与之前文章类似)
}
处理过程:
- 如果
ConnectionFactoryBean已经创建并放入一级缓存,getSingleton("connection")会返回ConnectionFactoryBean实例 - 调用
getObjectForBeanInstance(sharedInstance, "connection", "connection", null) - 注意 :这里传入的
name参数是原始的"connection"(没有&前缀)
步骤 3:getObjectForBeanInstance 处理 FactoryBean
源码位置 :AbstractBeanFactory.getObjectForBeanInstance()
java
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 步骤3:检查是否要获取FactoryBean本身
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果name以"&"开头,返回FactoryBean本身
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本身
}
// 步骤4:如果不是要获取FactoryBean本身,检查是否是FactoryBean
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance; // 不是FactoryBean,直接返回
}
// 步骤5:是FactoryBean,获取其创建的对象
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 从缓存中获取FactoryBean创建的对象
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 缓存中没有,调用FactoryBean.getObject()获取
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object; // 返回FactoryBean创建的对象
}
关键方法:BeanFactoryUtils.isFactoryDereference()
java
// BeanFactoryUtils.java
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
// BeanFactory.java
String FACTORY_BEAN_PREFIX = "&";
处理逻辑:
-
检查是否要获取 FactoryBean 本身:
- 调用
BeanFactoryUtils.isFactoryDereference(name)检查name是否以"&"开头 - 对于
"connection",isFactoryDereference("connection")返回false - 不走这个分支
- 调用
-
检查是否是 FactoryBean:
beanInstance是ConnectionFactoryBean实例,实现了FactoryBean接口- 走这个分支
-
获取 FactoryBean 创建的对象:
- 调用
getObjectFromFactoryBean(factory, beanName, !synthetic) - 内部会调用
factory.getObject()方法 - 返回
Connection对象
- 调用
关键点:
name参数是原始的"connection"(没有&前缀)isFactoryDereference("connection")返回false- 因此会调用
FactoryBean.getObject()获取目标对象
2.3 方式二:获取 FactoryBean 本身
调用代码:
java
FactoryBean<?> factoryBean = factory.getBean("&connection", FactoryBean.class);
执行流程:
步骤 1:进入 doGetBean 方法
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 步骤1:翻译Bean名称
final String beanName = transformedBeanName(name); // "connection"
// ... 后续步骤
}
处理过程:
- 传入的
name是"&connection"(有&前缀) transformedBeanName("&connection")会去除&前缀,返回"connection"beanName为"connection"- 但是 ,原始的
name参数("&connection")会保留,传递给后续方法
步骤 2:从缓存获取或创建 Bean
源码位置 :AbstractBeanFactory.doGetBean()
java
// AbstractBeanFactory.java
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name); // "connection"
// 步骤2:从缓存获取或创建Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果缓存中有,调用getObjectForBeanInstance处理FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
return bean;
}
// ... (如果缓存中没有,进入创建流程)
}
处理过程:
getSingleton("connection")返回ConnectionFactoryBean实例- 调用
getObjectForBeanInstance(sharedInstance, "&connection", "connection", null) - 注意 :这里传入的
name参数是原始的"&connection"(包含&前缀)
步骤 3:getObjectForBeanInstance 处理 FactoryBean
源码位置 :AbstractBeanFactory.getObjectForBeanInstance()
java
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 步骤3:检查是否要获取FactoryBean本身
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果name以"&"开头,返回FactoryBean本身
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本身
}
// ... (如果不是要获取FactoryBean本身,走其他逻辑)
}
处理逻辑:
-
检查是否要获取 FactoryBean 本身:
- 调用
BeanFactoryUtils.isFactoryDereference(name)检查name是否以"&"开头 - 对于
"&connection",isFactoryDereference("&connection")返回true - 走这个分支
- 调用
-
验证 beanInstance 是否是 FactoryBean:
beanInstance是ConnectionFactoryBean实例,实现了FactoryBean接口- 验证通过
-
返回 FactoryBean 本身:
- 直接返回
beanInstance(ConnectionFactoryBean实例) - 不会调用
getObject()方法
- 直接返回
关键点:
name参数是原始的"&connection"(包含&前缀)isFactoryDereference("&connection")返回true- 因此直接返回 FactoryBean 本身,不会调用
getObject()方法
3. 关键方法详解
3.1 transformedBeanName 方法
源码位置 :AbstractBeanFactory.transformedBeanName()
java
// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
return name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
// BeanFactory.java
String FACTORY_BEAN_PREFIX = "&";
作用:
- 去除
&前缀,得到实际的 Bean 名称 - 支持别名转换(通过
canonicalName()方法)
示例:
transformedBeanName("connection")→"connection"transformedBeanName("&connection")→"connection"
关键点:
- 虽然
transformedBeanName()会去除&前缀,但原始的name参数会保留 ,传递给getObjectForBeanInstance()方法 - 这样
getObjectForBeanInstance()就能知道是否要获取 FactoryBean 本身
3.2 isFactoryDereference 方法
源码位置 :BeanFactoryUtils.isFactoryDereference()
java
// BeanFactoryUtils.java
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
// BeanFactory.java
String FACTORY_BEAN_PREFIX = "&";
作用:
- 检查
name是否以"&"开头 - 如果以
"&"开头,说明要获取 FactoryBean 本身
示例:
isFactoryDereference("connection")→falseisFactoryDereference("&connection")→true
3.3 getObjectForBeanInstance 方法
源码位置 :AbstractBeanFactory.getObjectForBeanInstance()
java
// AbstractBeanFactory.java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 如果name以"&"开头,返回FactoryBean本身
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本身
}
// 如果不是FactoryBean,直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// 是FactoryBean,获取其创建的对象
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object; // 返回FactoryBean创建的对象
}
处理逻辑:
-
检查是否要获取 FactoryBean 本身:
- 如果
name以"&"开头,直接返回beanInstance(FactoryBean 本身)
- 如果
-
检查是否是 FactoryBean:
- 如果不是 FactoryBean,直接返回
beanInstance
- 如果不是 FactoryBean,直接返回
-
获取 FactoryBean 创建的对象:
- 如果是 FactoryBean,调用
getObjectFromFactoryBean()方法 - 内部会调用
FactoryBean.getObject()方法获取目标对象
- 如果是 FactoryBean,调用
4. 两种方式的对比
4.1 执行流程对比
| 步骤 | 方式一("connection") |
方式二("&connection") |
|---|---|---|
| 1. transformedBeanName | "connection" → "connection" |
"&connection" → "connection" |
| 2. getSingleton | 获取 ConnectionFactoryBean 实例 |
获取 ConnectionFactoryBean 实例 |
| 3. getObjectForBeanInstance | name = "connection" |
name = "&connection" |
| 4. isFactoryDereference | 返回 false |
返回 true |
| 5. 处理逻辑 | 调用 FactoryBean.getObject() |
直接返回 FactoryBean 本身 |
| 6. 返回结果 | Connection 对象 |
ConnectionFactoryBean 对象 |
4.2 关键区别
方式一:getBean("connection", Connection.class)
transformedBeanName("connection")返回"connection"getSingleton("connection")返回ConnectionFactoryBean实例getObjectForBeanInstance(..., "connection", ...)中:isFactoryDereference("connection")返回false- 调用
FactoryBean.getObject()方法 - 返回
Connection对象
方式二:getBean("&connection", FactoryBean.class)
transformedBeanName("&connection")返回"connection"getSingleton("connection")返回ConnectionFactoryBean实例getObjectForBeanInstance(..., "&connection", ...)中:isFactoryDereference("&connection")返回true- 直接返回
ConnectionFactoryBean实例 - 不会调用
getObject()方法
4.3 设计要点
-
&前缀的作用:&前缀用于区分是要获取 FactoryBean 本身还是 FactoryBean 创建的对象- 这是 Spring 的一个约定,
&是FACTORY_BEAN_PREFIX常量
-
transformedBeanName()的设计:- 虽然会去除
&前缀,但原始的name参数会保留 - 这样后续方法就能知道是否要获取 FactoryBean 本身
- 虽然会去除
-
getObjectForBeanInstance()的设计:- 根据
name参数是否包含&前缀来决定返回什么 - 如果包含
&前缀,直接返回 FactoryBean 本身 - 如果不包含
&前缀,调用getObject()方法获取目标对象
- 根据
5. 总结
5.1 核心机制
FactoryBean 的处理机制:
-
Bean 名称转换:
transformedBeanName()方法去除&前缀,得到实际的 Bean 名称- 但原始的
name参数会保留,传递给后续方法
-
FactoryBean 识别:
getObjectForBeanInstance()方法根据name参数是否包含&前缀来决定返回什么- 如果包含
&前缀,返回 FactoryBean 本身 - 如果不包含
&前缀,调用getObject()方法获取目标对象
-
缓存机制:
- FactoryBean 本身会被缓存(一级缓存)
- FactoryBean 创建的对象也会被缓存(如果
isSingleton()返回true)
5.2 关键方法
-
transformedBeanName():- 去除
&前缀,支持别名转换 - 返回实际的 Bean 名称
- 去除
-
isFactoryDereference():- 检查
name是否以"&"开头 - 用于判断是否要获取 FactoryBean 本身
- 检查
-
getObjectForBeanInstance():- 根据
name参数决定返回 FactoryBean 本身还是 FactoryBean 创建的对象 - 这是 FactoryBean 处理的核心方法
- 根据
5.3 使用场景
-
获取 FactoryBean 创建的对象:
- 使用
getBean("connection", Connection.class) - 适用于大多数场景,获取实际使用的对象
- 使用
-
获取 FactoryBean 本身:
- 使用
getBean("&connection", FactoryBean.class) - 适用于需要访问 FactoryBean 的方法或属性的场景
- 使用
5.4 注意事项
-
&前缀是 Spring 的约定:- 不要随意修改
FACTORY_BEAN_PREFIX常量 - 这是 Spring 框架的约定,修改可能导致兼容性问题
- 不要随意修改
-
FactoryBean 的缓存:
- FactoryBean 本身会被缓存
- FactoryBean 创建的对象是否缓存取决于
isSingleton()方法的返回值
-
类型检查:
- 如果
name以"&"开头,但beanInstance不是 FactoryBean,会抛出异常 - 这是 Spring 的类型安全检查
- 如果