spring的beanFactory和factoryBean
引言
在 Spring 框架中,存在两个名称极为相似的接口:BeanFactory 和 FactoryBean。由于命名接近,开发者在实际使用中容易产生混淆。本文将深入解析两者的定义、源码、使用场景及核心区别。
1. BeanFactory
BeanFactory 是 Spring IoC(Inversion of Control,控制反转)容器的顶层接口,定义了工厂规范。它提供了管理 Bean 的通用方法,如 getBean()、containsBean() 等。Spring 容器本质上都是该接口的具体实现,常见的实现类包括:
DefaultListableBeanFactoryXmlBeanFactory(已废弃)ApplicationContext(继承自ListableBeanFactory,间接实现BeanFactory)
这些实现类从不同维度提供了功能扩展,构成了 Spring 容器的核心基础。
1.1 接口源码
public interface BeanFactory {
// 对 FactoryBean 的转义定义。如果使用 bean 的名字检索 FactoryBean,得到的是工厂生成的对象;
// 如果需要得到工厂本身,需要加上此转义前缀。
String FACTORY_BEAN_PREFIX = "&";
// 根据 bean 的名字,获取在 IOC 容器中的 bean 实例
Object getBean(String name) throws BeansException;
// 根据 bean 的名字和 Class 类型来获取 bean 实例,增加了类型安全验证机制
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 检索 IOC 容器中是否包含指定名字的 bean
boolean containsBean(String name);
// 根据 bean 名字得到 bean 实例,并同时判断这个 bean 是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 得到 bean 实例的 Class 类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 得到 bean 的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}1.2 使用场景
- 从 IoC 容器中获取 Bean(支持按名称或按类型)。
- 检索 IoC 容器中是否包含指定的 Bean。
- 判断 Bean 的作用域(是否为单例)。
2. FactoryBean
FactoryBean 本身是一个 Bean,但它不仅仅是一个普通的 Bean。它是一个能生产或修饰对象生成的工厂 Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅限于自身,能返回任何 Bean 的实例。
2.1 接口源码
public interface FactoryBean<T> {
// 从工厂中获取 bean 实例
@Nullable
T getObject() throws Exception;
// 获取 Bean 工厂创建的对象的类型
@Nullable
Class<?> getObjectType();
// Bean 工厂创建的对象是否是单例模式
default boolean isSingleton() {
return true;
}
}从接口定义可以看出,FactoryBean 体现的是工厂的职责。如果一个 Bean A 实现了 FactoryBean 接口,那么 A 就变成了一个工厂。
- 根据 A 的名称获取到的实际上是工厂调用
getObject()返回的对象,而不是 A 本身。 - 如果要获取工厂 A 自身的实例,需要在名称前面加上
&符号(即BeanFactory.FACTORY_BEAN_PREFIX)。
具体获取规则如下:
getBean("name"):返回工厂生产的实例(getObject()的返回值)。getBean("&name"):返回工厂本身的实例(Bean A 自身)。
通常情况下,Bean 无须自己实现工厂模式,Spring 容器担任了工厂的角色;但在少数情况下,容器中的 Bean 本身就是工厂,作用是产生其他 Bean 实例。由工厂 Bean 产生的其他 Bean 实例,不再由 Spring 容器直接产生,因此与普通 Bean 的配置不同,不再需要提供 class 元素。
2.2 使用示例
首先定义一个 Bean 实现 FactoryBean 接口:
@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通过构造方法初始化实例";
}
@Override
public Object getObject() throws Exception {
// 这里并不一定要返回 MyBean 自身的实例,可以是其他任何对象的实例。
// 如 return new Student()...
return new MyBean("通过 FactoryBean.getObject() 创建实例");
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public String getMessage() {
return message;
}
}MyBean 实现了 FactoryBean 接口的方法。getObject() 可以返回任何对象的实例,这里测试返回 MyBean 自身实例,且在返回前给 message 字段赋值。同时在构造方法中也为 message 赋值。
测试代码中,先通过名称获取 Bean 实例,打印 message 的内容;再通过 & + 名称获取实例并打印 message 内容:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
MyBean myBean1 = (MyBean) context.getBean("myBean");
System.out.println("myBean1 = " + myBean1.getMessage());
MyBean myBean2 = (MyBean) context.getBean("&myBean");
System.out.println("myBean2 = " + myBean2.getMessage());
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}控制台输出结果:
myBean1 = 通过 FactoryBean.getObject() 初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false2.3 典型应用场景
为什么需要 FactoryBean?它在 Spring 中最为典型的应用就是用来创建 AOP 的代理对象。
AOP 实际上是 Spring 在运行时创建了一个代理对象。也就是说,这个对象是在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP 代理对象通过 Java 的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在 Spring 中就是——ProxyFactoryBean。
因此,FactoryBean 为我们实例化 Bean 提供了一个更为灵活的方式,我们可以通过 FactoryBean 创建出更为复杂的 Bean 实例。
3. 核心区别
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | Spring 容器的顶层接口,负责管理 Bean | 本质上是一个 Bean,由容器管理 |
| 角色 | 容器本身,负责生产和管理所有 Bean | 用户自定义的工厂,负责生产特定对象 |
| 获取方式 | 直接作为容器接口使用 | 需实现接口,通过 getObject() 获取对象 |
| 转义符号 | 定义了 & 前缀用于区分工厂本身 | 获取工厂本身时需加 & 前缀 |
| 关系 | 管理 FactoryBean | 被 BeanFactory 管理 |
简而言之:
- 两者都涉及工厂模式,但
FactoryBean本质上还是一个 Bean,也归BeanFactory管理。 BeanFactory是 Spring 容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口。
总结
BeanFactory 与 FactoryBean 的区别确实容易混淆,死记硬背是不行的,最好还是从源码层面,置于 Spring 的环境中去理解。
参考:
说明:文中提到的XmlBeanFactory实现类在 Spring 3.1 版本中已被标记为废弃(Deprecated),并在 Spring 4.0 中移除,建议在新项目中直接使用ApplicationContext相关实现。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/spring-de-beanfactory-he-factorybean.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。