Spring经典“送命题”:BeanFactory vs FactoryBean
故事场景:魔法玩具作坊
BeanFactory
— 玩具作坊本身
BeanFactory
(或者我们更常用的ApplicationContext
)就是整个玩具作坊。
• 它的身份:是老板,是管理者,是整个生产线的总和。它是容纳一切的容器。
• 它的工作:作坊的货架上陈列着各种可以直接生产的玩具,比如“泰迪熊”、“积木块”等(普通的Bean)。
• 互动方式:你作为顾客,走到作坊的前台(调用
context.getBean(...)
),对老板说:“给我来一个‘泰迪熊’”。老板(BeanFactory
)就会启动标准生产线,给你造一个泰迪熊。
BeanFactory
是提供产品(Bean)的工厂。
FactoryBean
— 作坊里的一台“多功能3D打印机”
现在,作坊里引进了一台非常神奇、非常复杂的“多功能3D打印机”(FactoryBean
)。
• 它的身份:它不是老板,它只是作坊里众多设备中的一台。它本身也是一个需要被老板管理和维护的“资产”(它本身也是一个Bean)。
• 它的特殊工作:这台3D打印机的唯一目的,就是生产其他更复杂的玩具,比如“变形金刚”。制造“变形金刚”的工艺太复杂了,标准生产线做不了,必须由这台3D打印机来定制生产。
• 神奇的互动方式:
1. 当你想要“变形金刚”时 (
context.getBean("transformerPrinter")
):
你走到前台,对老板说:“你好,我想要一台‘变形金刚’”。老板知道,“变形金刚”是由那台特殊的3D打印机(bean id为transformerPrinter
)生产的。
老板并不会把那台沉重的3D打印机推给你。
相反,他会走到3D打印机前,按下“打印”按钮(调用getObject()
方法)。机器一通操作后,一个崭新的“变形金刚”被生产出来。老板会把这个**“变形金刚”玩具(getObject()
返回的对象)**交给你。2. 当你想要“3D打印机”本身时 (
context.getBean("&transformerPrinter")
):
有一天,这台3D打印机坏了,你需要找个维修工来修。这时你走到前台,对老板说出了一句“暗号”:“你好,我要找的不是打印出来的玩具,而是那台ID前面带‘&’符号的‘&transformerPrinter’设备本身!”
老板听到暗号后,明白了你的意图,于是他不会去按打印按钮,而是会带你到后台,让你看到这台3D打印机本身(FactoryBean
实例)。
故事总结:
特性 | BeanFactory (玩具作坊) | FactoryBean (3D打印机) |
本质 | 容器 (Container)。Spring IoC的基础设施。 | Bean 。一个实现了特定接口的特殊Bean。 |
角色 | 工厂 。负责生产和管理所有的Bean。 | 工厂 Bean 。一个能生产其他对象的Bean。 |
getBean("id") 返回 | 返回id对应的Bean实例。 | 返回该FactoryBean生产的对象 (调用 |
getBean("&id") 返回 | (无此用法) | 返回该FactoryBean实例本身。 |
核心比喻 | 整个玩具作坊 | 作坊里的一台“玩具制造机” |
一句话总结 | 我是Spring的心脏,是所有Bean的妈。 | 我是一个特殊的Bean,我的工作是当另一个对象的妈。 |
结论:
BeanFactory
是Spring框架的根基,是管理者。而FactoryBean
是一种由你编写的、用于封装复杂对象创建逻辑的、被BeanFactory
所管理的特殊Bean。一个FactoryBean
活在BeanFactory
之中。
代码示例
// We'll use a simple Spring Boot setup for demonstration.
// File: Application.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@SpringBootApplication
public class FactoryDemoApplication {public static void main(String[] args) {// ApplicationContext IS-A BeanFactory. It's our container.ApplicationContext context = SpringApplication.run(FactoryDemoApplication.class, args);System.out.println("--- 演示 BeanFactory (容器) 的基本作用 ---");// We ask the container (BeanFactory) for a regular bean.String myMessage = context.getBean("myMessage", String.class);System.out.println("From BeanFactory, got myMessage: " + myMessage);System.out.println("\n--- 演示 FactoryBean 的特殊作用 ---");// 1. 获取由 FactoryBean 创建的对象// We ask for the bean named "myTool". Spring sees it's a FactoryBean.// It DOES NOT return the ToolFactoryBean instance.// Instead, it calls toolFactoryBean.getObject() and returns the result.Object toolObject = context.getBean("myTool");System.out.println("getBean(\"myTool\") 返回的对象类型: " + toolObject.getClass().getName()); // Will be com.example.Tool// 2. 获取 FactoryBean 本身// If we really want the factory itself, we must prefix the name with '&'.Object factoryObject = context.getBean("&myTool");System.out.println("getBean(\"&myTool\") 返回的对象类型: " + factoryObject.getClass().getName()); // Will be com.example.ToolFactoryBean}
}// File: AppConfig.java
@Configuration
class AppConfig {@Beanpublic String myMessage() {return "Hello from a simple bean!";}@Beanpublic ToolFactoryBean myTool() {return new ToolFactoryBean();}
}// File: Tool.java (The object we want the factory to produce)
import org.springframework.stereotype.Component;class Tool {private String name = "Screwdriver";public Tool() {System.out.println("A new Tool object has been created!");}// getters and setters...
}// File: ToolFactoryBean.java (The special factory bean)
import org.springframework.beans.factory.FactoryBean;class ToolFactoryBean implements FactoryBean<Tool> {@Overridepublic Tool getObject() throws Exception {// This is where the custom, complex object creation logic would go.System.out.println("ToolFactoryBean's getObject() is called. Creating and returning a Tool.");return new Tool();}@Overridepublic Class<?> getObjectType() {return Tool.class;}@Overridepublic boolean isSingleton() {return true;}
}