文章目录
-
- 前言
- [一、先看这行配置:一行 XML 引发的"血案"](#一、先看这行配置:一行 XML 引发的"血案")
- 二、想让这套跑起来,得满足仨条件
-
- [1. get() 必须是 static,这是硬性规定](#1. get() 必须是 static,这是硬性规定)
- [2. get() 的返回类型,必须和你 getBean 时想要的类型对得上](#2. get() 的返回类型,必须和你 getBean 时想要的类型对得上)
- [3. get() 要带参数,记住用 <constructor-arg>,别用 <property>](#3. get() 要带参数,记住用
,别用 )
- [三、静态工厂 vs 实例工厂,别把俩兄弟搞混](#三、静态工厂 vs 实例工厂,别把俩兄弟搞混)
- 四、BeanNotOfRequiredTypeException:程序员的深夜噩梦
-
- [1. 忘记写 factory-method](#1. 忘记写 factory-method)
- [2. get() 返回了工厂自己](#2. get() 返回了工厂自己)
- [3. 实例工厂配错了](#3. 实例工厂配错了)
- [4. 用了 FactoryBean,还搞错获取姿势](#4. 用了 FactoryBean,还搞错获取姿势)
- 五、来一套标准操作,照着抄就行
- 六、再报错?按这个顺序查,别乱开枪
- 七、最后总结一张表,面试前突击用

P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 传送门http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。
前言
先别急,今天要聊的事特别简单:Spring 怎么通过 factory-method 造 Bean。对,就是字面意义上的"造星星",不是让你当造物主,是让 Spring 当。这坑我踩过,你大概率也踩过------不然你也不会点进来,对吧?
一、先看这行配置:一行 XML 引发的"血案"
配置长这样:
xml
<bean id="starBean" class="com.xie.spring.StarFactory" factory-method="get" />
翻译成人话:Spring 拿到这行配置,会找到 com.xie.spring.StarFactory 这个类,敲敲门喊一嗓子:"老哥,把 get() 的返回值给我。"
然后,get() 返回啥,Spring 就把啥当成 starBean 塞进容器。
注意,重点来了:容器里 starBean 是啥类型,取决于 get() 返回啥,跟 StarFactory 这个类本身没有半毛钱关系。
这就好比你去食堂打饭,跟师傅说"来份番茄炒蛋",师傅给你打的是番茄炒蛋,而不是"师傅"本人。你要是想打一份食堂阿姨,那得换窗口,别在一棵树上吊死。
简单总结:Spring 自己不生产星星,它只是星星的搬运工。
二、想让这套跑起来,得满足仨条件
1. get() 必须是 static,这是硬性规定
java
public class StarFactory {
public static Star get() {
Star star = new Star();
star.setName("太阳");
return star;
}
}
看到 static 没?没它,Spring 当场甩脸子。
为啥?因为 Spring 压根没实例化 StarFactory,它连工厂的门都没进,就想隔空调用方法。让一个还没出生的对象去干活,这不光违反物理学,还违反 Spring 的脾气。我当年第一次忘了写 static,一个报错糊脸上,一度怀疑是自己 Java 没学明白。
2. get() 的返回类型,必须和你 getBean 时想要的类型对得上
java
Star star = context.getBean("starBean", Star.class);
你声明要 Star,get() 也老老实实返回 Star,大家相安无事。
但凡 get() 叛逆一点,返回个 StarFactory,Spring 当场给你表演一个 BeanNotOfRequiredTypeException。
那感觉就像你点了一杯奶茶,店员端上来一杯茶叶渣泡水,还安慰你:"都是茶,凑合喝吧。"凑合?我凑合你个大头鬼。
3. get() 要带参数,记住用 <constructor-arg>,别用 <property>
xml
<bean id="starBean" class="com.xie.spring.StarFactory" factory-method="get">
<constructor-arg name="name" value="太阳" />
</bean>
这里有个坑中坑:标签名虽然叫 constructor-arg(构造函数参数),实际传的是工厂方法的参数,跟构造方法一毛钱关系都没有。
名字像构造参数,干的却是传参的活,属于典型的"名字诈骗"。你要是在这写 <property>,Spring 会用沉默让你知道什么叫"无视你的配置"。
三、静态工厂 vs 实例工厂,别把俩兄弟搞混
刚才说了,get() 必须是静态的。那万一你手一抖,static 没写呢?
那就得换剧本:让 Spring 先把工厂实例化出来,再通过实例去调方法:
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),又是一个响亮的类型不匹配。
我愿称之为"买椟还珠"的 Spring 版:你要的是珍珠(Star),他给你个盒子(StarFactory),还觉得自己挺周到。
| 对比维度 | 静态工厂方法 | 实例工厂方法 |
|---|---|---|
| 工厂方法类型 | static | 非静态 |
| XML 配置 | class + factory-method | factory-bean + factory-method |
| 工厂要不要实例化 | 不要 | 要,还得让 Spring 管着 |
| 工厂能不能注入依赖 | 不能 | 能 |
四、BeanNotOfRequiredTypeException:程序员的深夜噩梦
这就是你之前遇到的那位老朋友:
Bean named 'starBean' is expected to be of type 'com.xie.spring.Star'
but was actually of type 'com.xie.spring.StarFactory'
翻译一下:你要 Star,容器里躺着 StarFactory。
容器的内心 OS:"你要的我没有,我有的你不要,要不咱俩都冷静冷静?"说真的,这报错信息长得像法院传票,我当年盯着看了半天才看懂:哦,原来你不是我要的那个人。
这错一般咋来的?常见四种情况:
1. 忘记写 factory-method
xml
<bean id="starBean" class="com.xie.spring.StarFactory" />
Spring 一看:哦,没让我调方法,那我直接把 StarFactory 实例化了吧。你想要的星星没了,容器里多了个工厂。这锅怪谁?怪你自己忘了传话。
2. get() 返回了工厂自己
java
public static StarFactory get() {
return new StarFactory(); // 返回的是工厂,不是产品
}
这就好比你去蛋糕店买蛋糕,店员把整个后厨(烤箱、面粉、围裙)打包给你了。是挺值,但不是你要的东西啊。
3. 实例工厂配错了
xml
<bean id="starBean" class="com.xie.spring.StarFactory" />
该写 factory-bean 引用工厂,结果写成了 class,Spring 直接给你实例化一个裸工厂。经典"抄错作业"现场。
4. 用了 FactoryBean,还搞错获取姿势
如果 StarFactory 实现了 FactoryBean<Star>:
getBean("starBean")→ 返回Star(产品)getBean("&starBean")→ 返回StarFactory(工厂本身)
& 一写错,C 语言老玩家直接狂喜:这不是取地址符吗?没错,& 就是让你"取工厂的地址",把藏在后面的工厂本体捞出来。这俩要是写反了,类型不匹配就是你的宿命。
五、来一套标准操作,照着抄就行
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='太阳'}
}
}
输出 Star{name='太阳'},完美。这颗星星,终于被正儿八经地从工厂里"生产"出来了。
顺便说一句,太阳是星星吗?天文学家说了,太阳就是一颗恒星。程序员表示:叫 Star 就行,别抬杠,能跑就行。
六、再报错?按这个顺序查,别乱开枪
- 打开 XML 配置文件,定位
id="starBean"那行。 - 看有没有
factory-method,没有?补上。 - 静态工厂,看
class是不是指向工厂类。 - 实例工厂,看是不是
factory-bean+factory-method,同时确认没写class。 - 用了
FactoryBean,看是不是用&starBean取工厂本体。 - 确认
get()返回类型是Star,不是StarFactory。 - 实在不行,先不指定类型,把 Bean 捞出来打印真实类型:
java
Object bean = context.getBean("starBean");
System.out.println(bean.getClass().getName());
输出 com.xie.spring.Star,恭喜,配置正确。输出 com.xie.spring.StarFactory,说明你的工厂方法把工厂自己"打包出售"了,回去改 get() 吧。
七、最后总结一张表,面试前突击用
| 维度 | 核心要点 |
|---|---|
| 配置含义 | 调用静态工厂方法 get(),用返回值当 Bean |
| 前提条件 | get() 得是 static,返回类型得是目标类型 |
| 参数传递 | 用 <constructor-arg>,不是 <property> |
| 实例工厂 | factory-bean + factory-method,不能再写 class |
| 常见错误 | BeanNotOfRequiredTypeException,容器里躺着工厂不是产品 |
| 排查方法 | 检查 factory-method、返回类型、获取姿势 |
| 正确获取 | getBean("starBean", Star.class) 拿产品;getBean("&starBean") 拿工厂 |
说白了,静态工厂、实例工厂、FactoryBean 这三兄弟,你就记住一句话:**Spring 要的是产品,不是生产产品的那个车间。**只要容器里躺的是你 get() 造出来的星星,而不是 StarFactory 本体,这个报错就跟你彻底说再见了。
P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 传送门http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。