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

文章来源声明: 原文作者:步行cgn; 来源站点:掘金; 原文链接:https://juejin.cn/post/7687133768550367258; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

一篇实用的排错指南,把静态工厂、实例工厂与 FactoryBean 的区别讲得清楚,适合正被 BeanNotOfRequiredTypeException 困扰的 Spring 开发者对照排查。

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

一、配置解读

你给出的配置片段是:

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

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

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

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

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

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

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

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

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

<span>Star</span> <span>star</span> <span>=</span> context.getBean(<span>"starBean"</span>, Star.class);

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

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

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

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

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

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

<span><!-- 先定义工厂 Bean --></span>
<span><<span>bean</span> <span>id</span>=<span>"starFactory"</span> <span>class</span>=<span>"com.xie.spring.StarFactory"</span>/></span>

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

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

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

四、常见错误:BeanNotOfRequiredTypeException

你之前遇到的报错是:

Bean named <span>'starBean'</span> <span>is</span> expected to be of <span>type</span> <span>'com.xie.spring.Star'</span> 
but was actually of <span>type</span> <span>'com.xie.spring.StarFactory'</span>

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

根本原因

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

  1. 忘记写 factory-method

    <span><<span>bean</span> <span>id</span>=<span>"starBean"</span> <span>class</span>=<span>"com.xie.spring.StarFactory"</span>/></span>
    
    

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

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

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

    <span><<span>bean</span> <span>id</span>=<span>"starBean"</span> <span>class</span>=<span>"com.xie.spring.StarFactory"</span>/></span>
    
    

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

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

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

五、完整正确示例

Java 类:

<span>public</span> <span>class</span> <span>Star</span> {
    <span>private</span> String name;
    <span>// getter/setter/toString</span>
}

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

beans.xml:

<span><?xml version=<span>"1.0"</span> encoding=<span>"UTF-8"</span>?></span>
<span><<span>beans</span> <span>xmlns</span>=<span>"http://www.springframework.org/schema/beans"</span>
       <span>xmlns:xsi</span>=<span>"http://www.w3.org/2001/XMLSchema-instance"</span>
       <span>xsi:schemaLocation</span>=<span>"http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"</span>></span>

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

<span></<span>beans</span>></span>

测试代码:

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

六、排查步骤

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

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

  3. 检查:

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

  5. 先不指定类型获取 Bean,打印实际类型: ``` Object bean = context.getBean("starBean"); System.out.println(bean.getClass().getName());

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

七、总结

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

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