1. 什么是 IoC 容器?------Spring 的核心理念
在进入 XmlBeanFactory 之前,我们需要首先理解一个重要的核心概念:IoC(Inversion of Control,控制反转)。
什么是 IoC?
IoC 是一种软件设计原则,它将对象的创建和依赖管理交由容器负责,从而降低代码之间的耦合度,提高程序的可测试性与可维护性。
在传统的 Java 应用中,类通常通过 new
关键字实例化依赖类,如下所示:
public class UserService {
private UserRepository userRepository = new UserRepository();
}
在 IoC 思维下,对象的创建交由容器完成,类仅声明所依赖的组件,由容器负责注入:
public class UserService {
private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
容器则通过配置将依赖注入进来。这种方式即是 IoC 的典型体现。
IoC 容器的核心职责
-
管理对象的生命周期
-
管理对象之间的依赖关系
-
提供统一的配置方式(如 XML、注解、JavaConfig)
Spring 就是 Java 世界中最知名的 IoC 容器框架之一。
2. BeanFactory 接口解析 ------ IoC 容器的灵魂
在 Spring 中,IoC 容器的顶层抽象就是 org.springframework.beans.factory.BeanFactory
接口。
BeanFactory 是什么?
BeanFactory 是 Spring IoC 容器的最基础接口,定义了容器的基本行为,例如:
-
getBean(String name)
:获取指定名称的 Bean 实例。 -
containsBean(String name)
:判断容器中是否包含某个 Bean。 -
isSingleton(String name)
:判断是否为单例。public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name);
// ...
}
为什么了解 BeanFactory 很重要?
所有更高级的 Spring 容器(如 ApplicationContext)都基于 BeanFactory 构建,理解 BeanFactory 是理解 Spring IoC 的基础。
3. 初识 XmlBeanFactory ------ 定义与功能概览
什么是 XmlBeanFactory?
XmlBeanFactory
是 Spring 框架早期版本中通过 XML 文件配置 Bean 的一种实现,它实现了 BeanFactory 接口,能够读取 XML 文件中定义的 Bean 元素并创建相应的实例。
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
reader.loadBeanDefinitions(resource);
}
}
从上面的源码可知:
-
XmlBeanFactory 继承自
DefaultListableBeanFactory
-
使用
XmlBeanDefinitionReader
解析 XML 文件 -
加载 BeanDefinition 并注册到当前容器
为什么还要了解 XmlBeanFactory?
尽管该类已在 Spring 3.1 之后被标记为废弃,但其原理仍然是 ApplicationContext 的重要基础。
4. 配置 XML 文件中的 Bean ------ 结构与常见属性详解
要使用 XmlBeanFactory,首先需要编写 XML 配置文件来定义 Bean。Spring 通过读取这些 XML 文件来构建 BeanDefinition 并生成 Bean 实例。
基本结构
一个标准的 Spring 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="userService" class="com.example.service.UserService"/>
</beans>
常见属性说明
1. id
和 name
-
id
:用于唯一标识 Bean 实例。 -
<bean id="myBean" name="alias1,alias2" class="com.example.MyClass"/>name
:可以为 Bean 提供多个别名。
2. class
用于指定该 Bean 的全限定类名,是 Spring 创建实例的依据。
<bean id="userDao" class="com.example.dao.UserDao"/>
3. scope
定义 Bean 的作用域,常见值有:
-
singleton
(默认):整个容器中只有一个实例。 -
<bean id="counter" class="com.example.Counter" scope="prototype"/>prototype
:每次调用都会创建一个新的实例。
4. init-method
和 destroy-method
用于定义 Bean 初始化和销毁时要调用的方法。
<bean id="resourceManager" class="com.example.ResourceManager"
init-method="init" destroy-method="cleanup"/>
5. depends-on
声明当前 Bean 依赖于其他 Bean,需要先初始化依赖的 Bean。
<bean id="beanA" class="com.example.BeanA" depends-on="beanB"/>
6. autowire
定义自动装配的方式:
-
byName
-
byType
-
constructor
-
<bean id="orderService" class="com.example.OrderService" autowire="byType"/>no
(默认)
属性注入(Property Injection)
通过 <property>
子元素设置属性值:
<bean id="userService" class="com.example.service.UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
<bean id="userRepository" class="com.example.repository.UserRepository"/>
构造函数注入(Constructor Injection)
<bean id="userService" class="com.example.service.UserService">
<constructor-arg ref="userRepository"/>
</bean>
5. 使用 XmlBeanFactory 加载 Bean 的实践示例
了解了 XML 配置的基础后,我们就可以使用 XmlBeanFactory
实际加载这些配置并获取 Bean 实例了。下面是详细的步骤和示例。
步骤一:准备 XML 配置文件
创建一个名为 beans.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="userRepository" class="com.example.repository.UserRepository"/>
<bean id="userService" class="com.example.service.UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
</beans>
步骤二:创建 Bean 类
UserRepository 类:
package com.example.repository;
public class UserRepository {
public void save() {
System.out.println("User saved.");
}
}
UserService 类:
package com.example.service;
import com.example.repository.UserRepository;
public class UserService {
private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser() {
System.out.println("Registering user...");
userRepository.save();
}
}
步骤三:使用 XmlBeanFactory 加载配置并使用 Bean
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import com.example.service.UserService;
public class MainApp {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService userService = (UserService) factory.getBean("userService");
userService.registerUser();
}
}
输出结果
Registering user...
User saved.
注意事项
-
XmlBeanFactory 会在调用
getBean
时才真正实例化 Bean,这被称为 延迟加载(Lazy Loading)。 -
如果配置错误(如 ID 拼写错误、类未找到),会在调用
getBean
时抛出异常。
6. XmlBeanFactory 的生命周期管理机制
在 Spring 框架中,每个 Bean 都拥有一个清晰的生命周期管理流程。XmlBeanFactory 作为最基础的容器,同样支持完整的 Bean 生命周期控制机制。理解这个生命周期对于正确地管理资源、初始化逻辑、释放内存等非常重要。
Bean 生命周期的阶段概览
一个 Bean 在容器中的典型生命周期如下:
-
实例化(Instantiation)
-
属性注入(Populate Properties)
-
Aware 接口回调(如 BeanNameAware、BeanFactoryAware 等)
-
BeanPostProcessor 前置处理
-
初始化方法执行(包括 InitializingBean 和 init-method)
-
BeanPostProcessor 后置处理
-
Bean 可用阶段
-
销毁阶段(包括 DisposableBean 和 destroy-method)
1. 实例化阶段
当调用 getBean()
方法时,XmlBeanFactory 首先根据 BeanDefinition 创建对象实例。此过程通过 Java 的反射机制完成:
Object bean = beanClass.getDeclaredConstructor().newInstance();
2. 属性注入
容器会将 XML 中通过 <property>
和 <constructor-arg>
指定的依赖注入到 Bean 中。
<bean id="userService" class="com.example.UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
对应的 Java 注入过程是通过反射调用 setUserRepository()
方法。
3. Aware 接口回调
如果 Bean 实现了如下接口之一,容器将会在实例化后回调相应方法:
-
BeanNameAware
:注入 Bean 的名称 -
BeanFactoryAware
:注入当前的 BeanFactory 实例 -
ApplicationContextAware
:注入 ApplicationContext(需要 ApplicationContext 支持)public class MyBean implements BeanNameAware {
public void setBeanName(String name) {
System.out.println("Bean name is: " + name);
}
}
4. BeanPostProcessor(前置处理)
如果注册了 BeanPostProcessor
,容器会在 Bean 初始化前调用 postProcessBeforeInitialization()
方法。
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 可对 bean 做增强处理,如代理、日志等
return bean;
}
5. 初始化方法执行
初始化方法有两种:
-
实现
InitializingBean
接口的afterPropertiesSet()
方法 -
XML 配置中的
init-method
属性指定的方法public class ResourceBean implements InitializingBean {
<bean id="resourceBean" class="com.example.ResourceBean" init-method="init"/>
public void afterPropertiesSet() throws Exception {
System.out.println("Bean 初始化完成");
}
}
6. BeanPostProcessor(后置处理)
Bean 初始化完成后,会再次进入 postProcessAfterInitialization()
方法,为 Bean 提供最后的扩展点。
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
7. Bean 使用阶段
此时 Bean 已完成初始化并可正常使用,由业务代码或容器进行调用。
8. Bean 销毁阶段
当容器关闭时,销毁流程开始。XmlBeanFactory 支持两种销毁方式:
-
实现
DisposableBean
接口的destroy()
方法 -
XML 中通过
destroy-method
指定的方法public class ResourceBean implements DisposableBean {
<bean id="resourceBean" class="com.example.ResourceBean" destroy-method="cleanup"/>
public void destroy() throws Exception {
System.out.println("Bean 被销毁");
}
}
⚠️ 注意:只有单例 Bean 的销毁方法才会被调用,原型作用域的 Bean 容器不会管理其销毁。
7. XmlBeanFactory 与 ApplicationContext 的深入比较
随着 Spring 发展,ApplicationContext
成为了主流的 IoC 容器,而 XmlBeanFactory
被逐渐淘汰。本节将从多个角度比较两者之间的异同。
基本定义对比
特性 | XmlBeanFactory | ApplicationContext |
---|---|---|
接口继承 | BeanFactory | BeanFactory + 多个接口 |
延迟加载 | 是 | 否(默认预加载单例 Bean) |
国际化支持 | 否 | 是(MessageSource) |
事件机制 | 无 | 支持事件发布机制 |
自动 BeanPostProcessor | 否(需手动注册) | 是 |
支持资源访问 | 部分 | 完整资源访问封装(ResourceLoader) |
推荐使用 | 否(已废弃) | 是(主流容器) |
示例对比:加载方式
使用 XmlBeanFactory
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyService service = factory.getBean("myService", MyService.class);
使用 ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyService service = context.getBean("myService", MyService.class);
生命周期差异
XmlBeanFactory
仅在调用 getBean()
时才创建 Bean,因此它具有 延迟加载(Lazy Initialization) 特性。而 ApplicationContext
会在启动时预加载所有非懒加载的单例 Bean。
这种行为差异带来如下影响:
-
XmlBeanFactory:适合资源紧张场景或按需初始化
-
ApplicationContext:启动时可能较慢,但能提前发现配置错误,且提供完整生命周期管理
BeanPostProcessor 自动注册
在 XmlBeanFactory
中,如果你定义了 BeanPostProcessor
,需要手动调用 addBeanPostProcessor()
方法进行注册。而在 ApplicationContext
中,这些后处理器会自动被注册并应用。
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
factory.addBeanPostProcessor(new CustomBeanPostProcessor());
国际化支持
ApplicationContext
实现了 MessageSource
接口,支持基于 messages.properties
文件的国际化功能,这在 XmlBeanFactory
中是不支持的。
String message = context.getMessage("welcome.message", null, Locale.CHINA);
事件发布机制
ApplicationContext
提供了事件发布和监听机制,可以发布自定义事件并监听容器内部事件,如 ContextRefreshedEvent。
context.publishEvent(new CustomEvent(this, "Hello World"));
资源访问封装
ApplicationContext
提供了统一的资源访问方式(如文件、URL、classpath等),而 XmlBeanFactory
仅限于 Resource
接口,不如前者强大灵活。
小结
|--------------------------|----------------|---------------------------------------|
| 对比点 | XmlBeanFactory | ApplicationContext |
| 是否推荐 | ❌ 不推荐 | ✅ 强烈推荐 |
| 生命周期支持 | ✅ 有限支持 | ✅ 完整支持 |
| 是否自动注册 BeanPostProcessor | ❌ 否 | ✅ 是 |
| 是否支持事件机制 | ❌ 否 | ✅ 是 |
| 是否支持国际化 | ❌ 否 | ✅ 是 |
| 是否支持注解扫描、配置类 | ❌ 否 | ✅ 是(@Configuration, @ComponentScan 等) |
从实践角度来看,除非特殊场景下进行底层定制或学习目的,推荐始终使用 ApplicationContext 作为 Spring 应用的主要容器。
8. 源码深度解析:XmlBeanFactory 的核心类与方法
XmlBeanFactory
是 Spring 框架中最早期的 XML 配置 Bean 容器实现,其继承体系如下:
XmlBeanFactory → DefaultListableBeanFactory → AbstractAutowireCapableBeanFactory → AbstractBeanFactory → FactoryBeanRegistrySupport → DefaultSingletonBeanRegistry
每一层都负责不同的职责。接下来我们逐层分析 XmlBeanFactory
及其核心功能实现源码。
8.1 类定义与构造器
源码路径(Spring 5.0 前):
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
说明:
-
XmlBeanFactory
实际上是DefaultListableBeanFactory
的一个装饰器,它添加了 XML 配置加载能力。 -
使用
XmlBeanDefinitionReader
加载 XML 配置。 -
loadBeanDefinitions
方法负责解析 Resource(如 ClassPathResource)中定义的 XML。
8.2 XmlBeanDefinitionReader 类
此类负责读取 XML 并将其转换为 BeanDefinition
。
核心方法:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
-
doLoadDocument()
:使用DocumentLoader
(默认是DefaultDocumentLoader
) 解析 XML 为 DOM。 -
registerBeanDefinitions()
:调用BeanDefinitionDocumentReader
对 DOM 结构进行 Bean 注册。
8.3 BeanDefinitionDocumentReader 与 BeanDefinitionParserDelegate
这些类处理 XML DOM 并将 <bean>
元素解析为 BeanDefinition
。
DefaultBeanDefinitionDocumentReader
核心方法:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createDelegate(readerContext, root);
parseBeanDefinitions(root, delegate);
}
parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
}
-
如果是默认命名空间(如
<bean>
),则调用parseDefaultElement
。 -
否则调用
parseCustomElement
(如 AOP、context 等命名空间)。
8.4 BeanDefinition 构建过程
进入 <bean>
元素的处理:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.readerContext.getRegistry());
}
最终将 BeanDefinition
放入 BeanFactory 的注册表中。
8.5 Bean 注册表结构
最终注册的 Bean 存储在 DefaultListableBeanFactory
中的两个核心结构:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
private final List<String> beanDefinitionNames = new ArrayList<>();
当调用 getBean("xxx")
时,流程如下:
-
从
beanDefinitionMap
获取对应的BeanDefinition
-
如果是单例,尝试从缓存中获取,否则创建
-
实例化对象(反射)
-
注入属性(依赖注入)
-
执行初始化方法和 Aware 接口
-
返回实例
9. Spring 如何解析 XML 配置文件?Schema 与命名空间的工作机制
除了基本的 <bean>
元素,Spring 配置文件常见的还有如 <context:component-scan>
、<aop:config>
等自定义标签。这些元素是通过 Spring 的 命名空间扩展机制(NamespaceHandler)实现的。
本章将揭示 Spring 如何通过 XSD 文件、NamespaceHandler 和 BeanDefinitionParser 机制解析复杂 XML。
9.1 XML 命名空间结构解析
Spring XML 文件顶部通常包含如下命名空间定义:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
核心含义:
-
xmlns
:定义 XML 的命名空间。 -
xsi:schemaLocation
:指定 XSD 文件位置。 -
Spring 使用
NamespaceHandler
对每个命名空间注册对应的解析器。
9.2 NamespaceHandler 的加载
META-INF/spring.handlers
Spring 使用 Java 的 SPI 机制加载 XML 命名空间处理器。
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
- 当解析
<context:component-scan>
时,Spring 会通过 URI 匹配加载ContextNamespaceHandler
。
NamespaceHandler 初始化流程:
-
解析 XML 根节点元素(如
<context:component-scan>
) -
查找命名空间 URI
-
使用
NamespaceHandlerResolver
查找对应类 -
实例化并调用
NamespaceHandler.init()
注册子标签解析器
9.3 BeanDefinitionParser 的作用
每个标签由具体的 BeanDefinitionParser
实现类处理。例如:
-
<context:component-scan>
对应ComponentScanBeanDefinitionParser
-
<aop:config>
对应ConfigBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute("base-package");
// 解析并注册 beanDefinition
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(...);
scanner.scan(basePackage);
}
9.4 自定义标签支持流程小结:
Spring 解析 XML 自定义标签的完整流程:
读取 XML → 分析命名空间 URI → spring.handlers → NamespaceHandler → 注册 BeanDefinitionParser → 解析标签 → 注册 BeanDefinition
9.5 示例:解析 context 命名空间
XML:
<context:component-scan base-package="com.example.service" />
解析流程:
-
识别命名空间
http://www.springframework.org/schema/context
-
spring.handlers 找到对应类
ContextNamespaceHandler
-
ContextNamespaceHandler.init()
注册ComponentScanBeanDefinitionParser
-
解析标签属性
base-package
-
使用
ClassPathBeanDefinitionScanner
扫描路径并注册 Bean
10. 深入理解 BeanDefinition ------ 容器中的元数据核心
BeanDefinition
是 Spring IoC 容器的基石,代表了容器中一个 bean 的抽象描述,它是 Spring 用于内部管理 bean 的元数据结构。
本章将深入探讨 BeanDefinition
的定义、结构、用途及扩展方式,帮助读者真正理解容器初始化和实例化过程中的核心逻辑。
10.1 BeanDefinition 的接口与实现
Spring 中 BeanDefinition 是一个接口,主要实现类有:
-
GenericBeanDefinition
-
RootBeanDefinition
-
ChildBeanDefinition
-
AnnotatedGenericBeanDefinition
常用的是 GenericBeanDefinition
,在基于 XML 或注解的配置中常被使用。
接口定义如下:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String getBeanClassName();
void setBeanClassName(String beanClassName);
String getScope();
void setScope(String scope);
boolean isLazyInit();
void setLazyInit(boolean lazyInit);
String[] getDependsOn();
void setDependsOn(String[] dependsOn);
boolean isAutowireCandidate();
void setAutowireCandidate(boolean autowireCandidate);
// 更多配置项...
}
10.2 核心字段说明
字段 | 含义 |
---|---|
beanClassName |
bean 的类全名 |
scope |
单例 (singleton ) 或原型 (prototype ) |
lazyInit |
是否延迟初始化 |
dependsOn |
本 bean 所依赖的其他 bean 名称 |
propertyValues |
注入的属性集合 |
constructorArgumentValues |
构造函数参数 |
initMethodName |
初始化方法名 |
destroyMethodName |
销毁方法名 |
10.3 BeanDefinitionHolder 与 BeanDefinitionReaderUtils
Spring 中不仅用 BeanDefinition
表示一个 bean,还通过 BeanDefinitionHolder
将其包装:
public class BeanDefinitionHolder {
private final BeanDefinition beanDefinition;
private final String beanName;
private final String[] aliases;
// 构造器、getters...
}
而 BeanDefinitionReaderUtils
则负责将定义注册到 BeanFactory 中:
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
}
10.4 从 XML 到 BeanDefinition 的转换示例
以如下 XML 为例:
<bean id="userService" class="com.example.UserService" scope="singleton" lazy-init="true">
<property name="userDao" ref="userDao" />
</bean>
Spring 将其解析为如下 BeanDefinition
对象:
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClassName("com.example.UserService");
bd.setScope(BeanDefinition.SCOPE_SINGLETON);
bd.setLazyInit(true);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("userDao", new RuntimeBeanReference("userDao"));
bd.setPropertyValues(pvs);
10.5 BeanDefinition 注册时机
Spring 加载 XML 配置时,解析器最终会调用:
BeanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
注册到容器的 bean 名称与定义会保存在:
// DefaultListableBeanFactory.java
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
此时 bean 尚未实例化,仅完成元数据注册。
10.6 BeanDefinition 与容器启动流程的关系
BeanDefinition 的生命周期大致如下:
-
加载配置(XML、注解等)
-
解析为 DOM 或扫描 Class 文件
-
生成 BeanDefinition
-
注册到 BeanFactory
-
容器调用
getBean()
时触发创建
10.7 自定义 BeanDefinition 示例
可以编程方式定义并注册一个 Bean:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(MyBean.class);
bd.setScope("singleton");
factory.registerBeanDefinition("myBean", bd);
MyBean myBean = (MyBean) factory.getBean("myBean");
这对于动态创建 Bean 非常实用,例如通过反射加载类。
11. XmlBeanFactory 的最佳实践与常见陷阱
尽管 XmlBeanFactory
在 Spring 早期版本中广泛使用,但随着 Spring Framework 的演进,它已被标记为 @Deprecated
。然而,在理解 Spring IoC 原理的学习阶段,它仍具有重要的参考价值。本章将从最佳实践与常见陷阱两个方面,为读者梳理 XmlBeanFactory 的使用策略。
11.1 最佳实践汇总
1)仅用于学习目的或轻量型场景
XmlBeanFactory
更适合用于:
-
学习 Spring IoC 初始化流程
-
实验项目或轻量容器(如工具类加载)
-
非 Web 场景或与 ApplicationContext 脱离的容器环境
2)加载资源时使用 ClassPathResource 或 FileSystemResource
推荐方式:
Resource resource = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(resource);
避免硬编码路径或使用未受支持的 URL 资源。
3)与 BeanFactoryPostProcessor 搭配使用
在初始化 BeanFactory 后,可以使用 BeanFactoryPostProcessor
扩展或修改 BeanDefinition:
factory.addBeanPostProcessor(new CustomBeanPostProcessor());
4)定义清晰的生命周期回调方法
使用 init-method
和 destroy-method
明确指定生命周期钩子:
<bean id="example" class="com.Example" init-method="init" destroy-method="cleanup" />
5)关注 Scope 与依赖注入合理性
明确指定 singleton
或 prototype
,并确保依赖注入方式与 Bean 的生命周期一致。
11.2 常见陷阱与规避方式
1)多次实例化导致资源浪费
错误方式:
XmlBeanFactory factory1 = new XmlBeanFactory(resource);
XmlBeanFactory factory2 = new XmlBeanFactory(resource); // 再次实例化
问题:每次实例化都重新解析 XML,效率低下。
✅ 解决:保持单例模式下的 BeanFactory 实例。
2)未处理 IoC 生命周期导致异常
如果某些 bean 依赖于容器级初始化顺序,而未使用 depends-on
明确顺序,可能导致初始化失败。
✅ 解决:添加 depends-on
明确依赖。
<bean id="dataSource" class="com.DataSource" />
<bean id="userDao" class="com.UserDao" depends-on="dataSource" />
3)Bean 定义重复导致覆盖或冲突
<bean id="userService" class="com.UserService" />
<bean id="userService" class="com.AnotherUserService" />
可能抛出异常或悄然覆盖。
✅ 解决:确保每个 bean 的 ID 唯一。
4)Resource 加载失败导致异常
Resource resource = new FileSystemResource("config/missing.xml");
文件不存在会导致 FileNotFoundException
。
✅ 解决:使用 try-catch 捕获并处理异常,或通过 ClassPathResource 确保路径正确。
5)误用 ApplicationContext 扩展功能
XmlBeanFactory
不支持事件发布、国际化、AOP 注解、@Value 等特性。
✅ 建议:在生产项目中使用 ClassPathXmlApplicationContext
替代。
11.3 XmlBeanFactory 的生命周期易错点
-
Bean 的初始化顺序未明确 => 添加
depends-on
-
单例/原型混用 => 保证注入时按需调整作用域或使用
ObjectFactory
-
销毁方法未触发 => 使用
registerShutdownHook()
或显式destroy()
((DisposableBean) bean).destroy();
11.4 替代方案建议
随着 Spring 的发展,推荐使用 ApplicationContext
作为默认容器,特别是:
-
ClassPathXmlApplicationContext
-
AnnotationConfigApplicationContext
-
GenericApplicationContext
这些容器提供更多功能与扩展性。
小结
实践 | 建议 |
---|---|
是否适合生产使用? | ❌ 不推荐 |
是否适合学习使用? | ✅ 强烈推荐 |
是否支持完整容器功能? | ❌ 功能受限 |
是否线程安全? | ✅ 多数情况下线程安全 |
XmlBeanFactory
是理解 Spring IoC 的经典入口,但实际开发应优先使用更现代的 ApplicationContext
系列容器。