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 对象。常见情况有:
-
忘记写
factory-methodxml<bean id="starBean" class="com.xie.spring.StarFactory"/>Spring 直接实例化
StarFactory,自然类型不匹配。 -
factory-method方法返回了工厂自身javapublic static StarFactory get() { return new StarFactory(); // 返回的是工厂,不是产品 } -
实例工厂方法配置错误
xml<bean id="starBean" class="com.xie.spring.StarFactory"/>应该写成
factory-bean引用,而不是直接写class。 -
使用
FactoryBean接口时获取方式错误 如果StarFactory实现了FactoryBean<Star>,那么:getBean("starBean")返回StargetBean("&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='太阳'}
}
}
六、排查步骤
-
打开 Spring 的 XML 配置文件。
-
找到
id="starBean"的 Bean 定义。 -
检查:
- 是否使用了
factory-method? - 如果是静态工厂,
class是否指向工厂类? - 如果是实例工厂,是否配置了
factory-bean和factory-method? - 如果实现了
FactoryBean,是否通过&starBean获取工厂?
- 是否使用了
-
确认工厂方法的返回类型是
Star,而不是StarFactory。 -
先不指定类型获取 Bean,打印实际类型:
javaObject 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 的区别,确保容器中存放的是工厂方法返回的产品,而不是工厂本身,就能避免这类错误。