深入理解 Spring 中的 XmlBeanFactory 原理及实践
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 实例。name
:可以为 Bean 提供多个别名。
<bean id="myBean" name="alias1,alias2" class="com.example.MyClass"/>
2. class
用于指定该 Bean 的全限定类名,是 Spring 创建实例的依据。
<bean id="userDao" class="com.example.dao.UserDao"/>
3. scope
定义 Bean 的作用域,常见值有:
singleton
(默认):整个容器中只有一个实例。prototype
:每次调用都会创建一个新的实例。
<bean id="counter" class="com.example.Counter" scope="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
no
(默认)
<bean id="orderService" class="com.example.OrderService" autowire="byType"/>
属性注入(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 {public void afterPropertiesSet() throws Exception {System.out.println("Bean 初始化完成");}
}<!-- XML 配置 -->
<bean id="resourceBean" class="com.example.ResourceBean" init-method="init"/>
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 {public void destroy() throws Exception {System.out.println("Bean 被销毁");}
}<!-- XML 配置 -->
<bean id="resourceBean" class="com.example.ResourceBean" destroy-method="cleanup"/>
⚠️ 注意:只有单例 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.xsdhttp://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");// 解析并注册 beanDefinitionClassPathBeanDefinitionScanner 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
系列容器。