【Spring源码】getBean源码实战(四)——FactoryBean

getBean源码实战(四)------FactoryBean

代码仓库Gitee 仓库链接

本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。

1. FactoryBean 简介

1.1 什么是 FactoryBean

FactoryBean 是 Spring 提供的一个特殊接口,用于创建复杂的 Bean。当我们需要创建的对象比较复杂,或者需要额外的初始化逻辑时,可以使用 FactoryBean

核心特点

  1. getObject() 方法返回实际要创建的 Bean 实例
  2. getObjectType() 返回 Bean 的类型
  3. 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 = "&";

处理逻辑

  1. 检查是否有 & 前缀

    • 如果 name"&" 开头,说明要获取 FactoryBean 本身
    • 如果 name 不以 "&" 开头,说明要获取 FactoryBean 创建的对象
  2. 去除 & 前缀

    • 如果以 "&" 开头,调用 substring(1) 去除前缀,得到实际的 Bean 名称
    • 例如:"&connection""connection"
  3. 别名转换

    • 调用 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 = "&";

处理逻辑

  1. 检查是否要获取 FactoryBean 本身

    • 调用 BeanFactoryUtils.isFactoryDereference(name) 检查 name 是否以 "&" 开头
    • 对于 "connection"isFactoryDereference("connection") 返回 false
    • 不走这个分支
  2. 检查是否是 FactoryBean

    • beanInstanceConnectionFactoryBean 实例,实现了 FactoryBean 接口
    • 走这个分支
  3. 获取 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本身,走其他逻辑)
}

处理逻辑

  1. 检查是否要获取 FactoryBean 本身

    • 调用 BeanFactoryUtils.isFactoryDereference(name) 检查 name 是否以 "&" 开头
    • 对于 "&connection"isFactoryDereference("&connection") 返回 true
    • 走这个分支
  2. 验证 beanInstance 是否是 FactoryBean

    • beanInstanceConnectionFactoryBean 实例,实现了 FactoryBean 接口
    • 验证通过
  3. 返回 FactoryBean 本身

    • 直接返回 beanInstanceConnectionFactoryBean 实例)
    • 不会调用 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")false
  • isFactoryDereference("&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创建的对象
}

处理逻辑

  1. 检查是否要获取 FactoryBean 本身

    • 如果 name"&" 开头,直接返回 beanInstance(FactoryBean 本身)
  2. 检查是否是 FactoryBean

    • 如果不是 FactoryBean,直接返回 beanInstance
  3. 获取 FactoryBean 创建的对象

    • 如果是 FactoryBean,调用 getObjectFromFactoryBean() 方法
    • 内部会调用 FactoryBean.getObject() 方法获取目标对象

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)

  1. transformedBeanName("connection") 返回 "connection"
  2. getSingleton("connection") 返回 ConnectionFactoryBean 实例
  3. getObjectForBeanInstance(..., "connection", ...) 中:
    • isFactoryDereference("connection") 返回 false
    • 调用 FactoryBean.getObject() 方法
    • 返回 Connection 对象

方式二:getBean("&connection", FactoryBean.class)

  1. transformedBeanName("&connection") 返回 "connection"
  2. getSingleton("connection") 返回 ConnectionFactoryBean 实例
  3. getObjectForBeanInstance(..., "&connection", ...) 中:
    • isFactoryDereference("&connection") 返回 true
    • 直接返回 ConnectionFactoryBean 实例
    • 不会调用 getObject() 方法

4.3 设计要点

  1. & 前缀的作用

    • & 前缀用于区分是要获取 FactoryBean 本身还是 FactoryBean 创建的对象
    • 这是 Spring 的一个约定,&FACTORY_BEAN_PREFIX 常量
  2. transformedBeanName() 的设计

    • 虽然会去除 & 前缀,但原始的 name 参数会保留
    • 这样后续方法就能知道是否要获取 FactoryBean 本身
  3. getObjectForBeanInstance() 的设计

    • 根据 name 参数是否包含 & 前缀来决定返回什么
    • 如果包含 & 前缀,直接返回 FactoryBean 本身
    • 如果不包含 & 前缀,调用 getObject() 方法获取目标对象

5. 总结

5.1 核心机制

FactoryBean 的处理机制

  1. Bean 名称转换

    • transformedBeanName() 方法去除 & 前缀,得到实际的 Bean 名称
    • 但原始的 name 参数会保留,传递给后续方法
  2. FactoryBean 识别

    • getObjectForBeanInstance() 方法根据 name 参数是否包含 & 前缀来决定返回什么
    • 如果包含 & 前缀,返回 FactoryBean 本身
    • 如果不包含 & 前缀,调用 getObject() 方法获取目标对象
  3. 缓存机制

    • FactoryBean 本身会被缓存(一级缓存)
    • FactoryBean 创建的对象也会被缓存(如果 isSingleton() 返回 true

5.2 关键方法

  1. transformedBeanName()

    • 去除 & 前缀,支持别名转换
    • 返回实际的 Bean 名称
  2. isFactoryDereference()

    • 检查 name 是否以 "&" 开头
    • 用于判断是否要获取 FactoryBean 本身
  3. getObjectForBeanInstance()

    • 根据 name 参数决定返回 FactoryBean 本身还是 FactoryBean 创建的对象
    • 这是 FactoryBean 处理的核心方法

5.3 使用场景

  1. 获取 FactoryBean 创建的对象

    • 使用 getBean("connection", Connection.class)
    • 适用于大多数场景,获取实际使用的对象
  2. 获取 FactoryBean 本身

    • 使用 getBean("&connection", FactoryBean.class)
    • 适用于需要访问 FactoryBean 的方法或属性的场景

5.4 注意事项

  1. & 前缀是 Spring 的约定

    • 不要随意修改 FACTORY_BEAN_PREFIX 常量
    • 这是 Spring 框架的约定,修改可能导致兼容性问题
  2. FactoryBean 的缓存

    • FactoryBean 本身会被缓存
    • FactoryBean 创建的对象是否缓存取决于 isSingleton() 方法的返回值
  3. 类型检查

    • 如果 name"&" 开头,但 beanInstance 不是 FactoryBean,会抛出异常
    • 这是 Spring 的类型安全检查

相关推荐
@淡 定7 小时前
消息积压处理
java
刘一说7 小时前
Spring Boot与MyBatis整合原理及事务管理:深度解析与实战指南
spring boot·后端·mybatis
Yu_iChan7 小时前
苍穹外卖Day2 分类管理功能
java·intellij-idea·postman
wen__xvn7 小时前
代码随想录算法训练营DAY3第一章 数组part02
java·数据结构·算法
Program Debug7 小时前
Mac安装JDK
java·开发语言·macos
计算机毕设指导67 小时前
基于微信小程序的家政服务与互助平台【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
一起养小猫7 小时前
LeetCode100天Day8-缺失数字与只出现一次的数字
java·数据结构·算法·leetcode
Coder_Boy_7 小时前
基于SpringAI企业级智能教学考试平台智能作业模块全业务闭环方案
java·人工智能·spring·spring cloud
IT_陈寒7 小时前
SpringBoot性能翻倍秘籍:5个被低估的配置项让我QPS提升200%
前端·人工智能·后端