Spring 通过 factory-method 实例化 Bean 详解与常见错误排查

Spring 通过 factory-method 实例化 Bean 详解与常见错误排查

一、配置解读

你给出的配置片段是:

xml 复制代码
<bean id="starBean" class="com.xie.spring.StarFactory" factory-method="get">
</bean>

这段配置的含义是:Spring 会调用 com.xie.spring.StarFactory 类中的静态方法 get(),用它的返回值作为容器中 starBean 这个 Bean 的实例。

也就是说,容器里 starBean 的类型取决于 get() 方法的返回类型,而不是 StarFactory 这个类本身。

二、静态工厂方法实例化的前提条件

要让这段配置正常工作,必须满足以下条件:

1. get() 必须是静态方法

java 复制代码
public class StarFactory {
    public static Star get() {
        Star star = new Star();
        star.setName("太阳");
        return star;
    }
}

如果 get() 不是 static 的,Spring 启动时会报错,因为它无法在没有工厂实例的情况下调用实例方法。

2. get() 的返回类型必须与 getBean() 时的期望类型一致

java 复制代码
Star star = context.getBean("starBean", Star.class);

如果 get() 返回的是 Star,注入成功。如果返回的是 StarFactory 或其他类型,就会抛出 BeanNotOfRequiredTypeException。

3. 如果 get() 有参数,必须用 <constructor-arg> 传递

xml 复制代码
<bean id="starBean" class="com.xie.spring.StarFactory" factory-method="get">
    <constructor-arg name="name" value="太阳"/>
</bean>

注意:这里虽然标签名是 constructor-arg,但实际是传给工厂方法的参数,不是构造方法参数。不能用 <property>。

三、与实例工厂方法的对比

如果 get() 不是静态方法,就必须改用实例工厂写法:

xml 复制代码
<!-- 先定义工厂 Bean -->
<bean id="starFactory" class="com.xie.spring.StarFactory"/>

<!-- 再通过工厂实例调用方法 -->
<bean id="starBean" factory-bean="starFactory" factory-method="get"/>

此时第二个 <bean> 不能再写 class 属性。否则容器里存放的将是 StarFactory 实例,而不是 get() 返回的产品,调用 getBean("starBean", Star.class) 时就会报类型不匹配。

对比维度 静态工厂方法 实例工厂方法
工厂方法类型 static 非静态
XML 配置 class + factory-method factory-bean + factory-method
工厂是否需要实例化 不需要 需要,且由 Spring 管理
工厂能否注入依赖 不能 可以

四、常见错误:BeanNotOfRequiredTypeException

你之前遇到的报错是:

python 复制代码
Bean named 'starBean' is expected to be of type 'com.xie.spring.Star' 
but was actually of type 'com.xie.spring.StarFactory'

这个异常表示:你试图从容器中获取名为 starBean 的 Bean,期望类型是 Star,但容器中实际存放的却是 StarFactory 类型的实例。

根本原因

出现这个错误,通常是因为配置或代码中,starBean 被定义成了 StarFactory 本身,而不是通过工厂方法创建出的 Star 对象。常见情况有:

  1. 忘记写 factory-method

    xml 复制代码
    <bean id="starBean" class="com.xie.spring.StarFactory"/>

    Spring 直接实例化 StarFactory,自然类型不匹配。

  2. factory-method 方法返回了工厂自身

    java 复制代码
    public static StarFactory get() {
        return new StarFactory();  // 返回的是工厂,不是产品
    }
  3. 实例工厂方法配置错误

    xml 复制代码
    <bean id="starBean" class="com.xie.spring.StarFactory"/>

    应该写成 factory-bean 引用,而不是直接写 class。

  4. 使用 FactoryBean 接口时获取方式错误 如果 StarFactory 实现了 FactoryBean<Star>,那么:

    • getBean("starBean") 返回 Star
    • getBean("&starBean") 返回 StarFactory 写反了就会类型不匹配。

五、完整正确示例

Java 类:

java 复制代码
public class Star {
    private String name;
    // getter/setter/toString
}

public class StarFactory {
    public static Star get() {
        Star star = new Star();
        star.setName("太阳");
        return star;
    }
}

beans.xml:

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">

    <bean id="starBean" class="com.xie.spring.StarFactory" factory-method="get"/>

</beans>

测试代码:

java 复制代码
public class testFirstBean {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Star star = context.getBean("starBean", Star.class);
        System.out.println(star);  // Star{name='太阳'}
    }
}

六、排查步骤

  1. 打开 Spring 的 XML 配置文件。

  2. 找到 id="starBean" 的 Bean 定义。

  3. 检查:

    • 是否使用了 factory-method?
    • 如果是静态工厂,class 是否指向工厂类?
    • 如果是实例工厂,是否配置了 factory-bean 和 factory-method?
    • 如果实现了 FactoryBean,是否通过 &starBean 获取工厂?
  4. 确认工厂方法的返回类型是 Star,而不是 StarFactory。

  5. 先不指定类型获取 Bean,打印实际类型:

    java 复制代码
    Object bean = context.getBean("starBean");
    System.out.println(bean.getClass().getName());

    如果输出 com.xie.spring.Star,说明配置正确;如果输出 com.xie.spring.StarFactory,说明工厂方法返回了工厂自身。

七、总结

维度 核心要点
配置含义 调用静态工厂方法 get(),用返回值作为 Bean
前提条件 get() 必须是 static,返回类型必须是目标类型
参数传递 用 <constructor-arg>,不是 <property>
实例工厂 用 factory-bean + factory-method,不能再写 class
常见错误 BeanNotOfRequiredTypeException,容器中存放的是工厂而非产品
排查方法 检查 factory-method、返回类型、获取方式
正确获取 getBean("starBean", Star.class) 获取产品;getBean("&starBean") 获取工厂(仅 FactoryBean 场景)

理解静态工厂、实例工厂和 FactoryBean 的区别,确保容器中存放的是工厂方法返回的产品,而不是工厂本身,就能避免这类错误。

相关推荐
香蕉大香蕉(06敬畏市场)12 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习
小小张说故事12 小时前
Python 的 str 为什么设计成不可变?拆开字符串驻留、字符串池,以及 += 拼接到底慢在哪
后端·python
LaughingZhu12 小时前
Product Hunt 热榜 2026-10-30:Empacer、Scape、Floot in ChatGPT
java·前端·数据库·人工智能·chatgpt·notion
AI架构师之家12 小时前
我把 Hermes 这匹"马"养了两周,它干了一件让我有点发毛的事
后端
dpharness12 小时前
块协议用了两周,我把九个坑按顺序记下来了
后端
niucloud-admin12 小时前
NIUCLOUD PHP SAAS上门家政使用文档:服务保障
java·开发语言·php
加宝的起点雷达13 小时前
跑了两周的信号日报,我把降级链改了四次
人工智能·后端
IT枫斗者枫哥13 小时前
CompletableFuture失败后返回空列表:异常没了,业务含义也变了
java·后端
Gopher_HBo13 小时前
opentelemetrySDK源码解析
后端
知识搬运临时工13 小时前
一个模板把 AEM 拖进 Full GC 死亡螺旋:300% CPU、7 GB 堆、310 万行堆栈
java·aws