工厂模式 + Spring FactoryBean
一句话结论(30s)
Spring FactoryBean 的本质是「Bean 工厂本身也是 Bean,但 getBean("xxx") 拿到的是它生产的产品」。其关键设计是把复杂对象的创建逻辑从 @Bean 方法里冗长的 new 代码封装进独立的 FactoryBean 类,产品在真正需要时才通过 getObject() 懒创建。代价是获取工厂本身要加 & 前缀——因为 getBean("name") 默认返回产品而非工厂实例。
核心原理(2min)
主流程:容器 BeanDefinition 里注册的是 FactoryBean 本身的定义,用户 ctx.getBean(SqlSessionFactory.class) 时 Spring 调用 FactoryBean 的 getObject() 返回产品(如 MyBatis 解析 XML 后构建出的 SqlSessionFactory);若要拿工厂本身,则用 ctx.getBean("&sqlSessionFactory", SqlSessionFactoryBean.class),& 由 BeanFactoryUtils.FACTORY_BEAN_PREFIX 定义。关键机制是「生产与使用分离」——普通 @Bean 方法直接 new 复杂对象会让配置代码冗长,而 FactoryBean 把「如何创建」封装进独立类,产品按需懒创建;这背后对应三种工厂模式的取舍:简单工厂加产品要改 if-else(违背开闭),工厂方法加产品只需加一个工厂(开闭但类数量膨胀),抽象工厂一次生产一个产品族。
底层深入(5-10min)
三种工厂
- 简单工厂:一个工厂类 +
if-else选择产品 → 新增产品要改工厂(违背开闭原则) - 工厂方法:每种产品一个工厂类 → 新增产品只需加一个工厂(开闭)→ 类数量 N×M
- 抽象工厂:一个工厂生产一个产品族(如 MySQL 系列 = MySQL Connection + MySQL Statement + MySQL ResultSet 全部配套)
💭 思考穿插:工厂和抽象工厂到底什么区别?——工厂方法是一个工厂只造一种产品,加产品就加一个工厂;抽象工厂是一个工厂造「一整套必须互相配套的产品族」。判断标准:如果你一次要的是「单个产品」,那是工厂方法;如果一次要的是「一组必须搭配使用的产品」,那是抽象工厂。而简单工厂其实不算严格的设计模式,它只是把 if-else 从调用方挪进了工厂类里。
Spring FactoryBean
FactoryBean 是一种特殊的 Bean——Bean 工厂本身就是 Bean,但 getBean("xxx") 拿到的是工厂生产的产品:
@Component("sqlSessionFactory")
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
@Override
public SqlSessionFactory getObject() {
// MyBatis 解析 XML 配置 + 构建 SqlSessionFactory
return new SqlSessionFactoryBuilder().build(configuration);
}
@Override
public Class<?> getObjectType() {
return SqlSessionFactory.class;
}
}
// 获取产品
SqlSessionFactory factory = ctx.getBean(SqlSessionFactory.class);
// 获取工厂本身
SqlSessionFactoryBean bean = ctx.getBean("&sqlSessionFactory", SqlSessionFactoryBean.class);
& 前缀获取工厂实例而非产品——由 BeanFactoryUtils.FACTORY_BEAN_PREFIX = "&" 定义。
💭 思考穿插:为什么
getBean("xxx")拿到的是产品而不是工厂本身?——因为容器的目标是「给你能用的对象」,工厂只是手段、产品才是目的。这也带来一个反直觉点:工厂自己也是个 Bean,但要拿到它得加&前缀。记住一条口诀:默认拿产品,加&拿工厂。
FactoryBean 接口的真实定义
上面 SqlSessionFactoryBean 实现的 FactoryBean<T> 接口,核心契约只有三个方法(节选自 org.springframework.beans.factory.FactoryBean,Javadoc 已省略):
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable T getObject() throws Exception;
@Nullable Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
getObject() 是灵魂——返回工厂生产的产品实例,@Nullable 表示允许返回 null,throws Exception 让创建过程中的异常直接抛给容器。容器拿到 Bean 定义后,并不在注册时创建产品,而是等到某个 Bean 真正被 getBean() 请求时才回调 getObject(),这就是「FactoryBean 延迟创建」的接口级依据。
getObjectType() 让容器不实例化对象就能知道产品类型,供按类型自动装配(@Autowired / getBeansOfType)匹配;isSingleton() 默认返回 true,告诉容器产品是单例、getObject() 的返回值可以缓存复用。三个方法配合,容器就能把「如何创建复杂对象」交给 FactoryBean,自己只管按需调用与缓存。
💭 思考穿插:为什么产品不提前创建、要等到
getBean()才调getObject()?——因为复杂对象的创建成本高(解析 XML、建连接池),提前建可能白建。getObjectType()让容器「不实例化就知道类型」,isSingleton()让容器「建一次就能缓存」,三者配合才实现「按需懒创建 + 类型匹配 + 复用」。
为什么需要 FactoryBean
普通 @Bean 方法在用 new 创建复杂的、需要大量配置的对象时,配置代码冗长。FactoryBean 把”如何创建这个复杂对象”封装到独立的类中,容器的 BeanDefinition 中注册的是 FactoryBean 本身的定义,产品在需要时才通过 getObject() 懒创建。
💭 思考穿插:既然
@Bean方法也能new出复杂对象,为什么还要 FactoryBean?——区别在「复杂度封装」。@Bean里塞几十行构建逻辑,配置类和业务纠缠;FactoryBean 把「如何创建」抽成独立类,配置类只管声明。当创建逻辑需要复用、需要独立测试、需要延迟到真正使用时,FactoryBean 更干净。
章末提问
Q1:简单工厂、工厂方法、抽象工厂有什么区别?
结论先行:简单工厂是一个工厂用 if-else 造多种产品,工厂方法是一种产品一个工厂,抽象工厂是一个工厂造一套互相配套的产品族。 因为:简单工厂加产品要改 if-else(违背开闭);工厂方法加产品只加一个工厂(开闭,但类数量膨胀);抽象工厂强调「产品之间的配套约束」,一次产出同一族的全部产品。
Q2:getBean("xxx") 拿到的是产品还是工厂?怎么拿工厂?
结论先行:拿到的是产品(
getObject()的返回值);要拿工厂本身得加&前缀。 因为:容器默认把 FactoryBean 当产品的「生产源」,getBean("name")命中 FactoryBean 时返回getObject()的结果;&由BeanFactoryUtils.FACTORY_BEAN_PREFIX定义,专用于「我要工厂本体」这种少数场景。
Q3:FactoryBean 和 BeanFactory 有什么区别?
结论先行:BeanFactory 是容器本身(管理所有 Bean),FactoryBean 是容器里的一个特殊 Bean,用来生产某个产品。 因为:一个字母、两个维度——BeanFactory 是「管理 Bean 的工厂」,FactoryBean 是「被 BeanFactory 管理的、生产另一个对象的 Bean」。对方常拿名字相似来挖坑。
Q4:产品为什么懒创建?getObjectType() 和 isSingleton() 各起什么作用?
结论先行:懒创建是为了省成本;
getObjectType()让容器不实例化就能按类型装配;isSingleton()让容器缓存复用产品。 因为:复杂对象(解析 XML、建连接池)创建成本高,等真正getBean()才调getObject();getObjectType()供@Autowired/getBeansOfType匹配;isSingleton()默认 true,让getObject()只调一次、返回值可复用。
Q5:什么时候用 FactoryBean 而不是 @Bean?
结论先行:创建逻辑复杂、需要复用/独立测试/延迟创建时用 FactoryBean。 因为:
@Bean方法里塞几十行构建代码会让配置类冗长且难测试;FactoryBean 把「如何创建」抽成独立类,产品按需懒创建。