深入理解 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. idname
  • 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-methoddestroy-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 在容器中的典型生命周期如下:

  1. 实例化(Instantiation)

  2. 属性注入(Populate Properties)

  3. Aware 接口回调(如 BeanNameAware、BeanFactoryAware 等)

  4. BeanPostProcessor 前置处理

  5. 初始化方法执行(包括 InitializingBean 和 init-method)

  6. BeanPostProcessor 后置处理

  7. Bean 可用阶段

  8. 销毁阶段(包括 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 初始化完成");
    }
    }

    <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 被销毁");
    }
    }

    <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);
    }
}
说明:
  1. XmlBeanFactory 实际上是 DefaultListableBeanFactory 的一个装饰器,它添加了 XML 配置加载能力。

  2. 使用 XmlBeanDefinitionReader 加载 XML 配置。

  3. 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);
}
  1. doLoadDocument():使用 DocumentLoader(默认是 DefaultDocumentLoader) 解析 XML 为 DOM。

  2. 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") 时,流程如下:

  1. beanDefinitionMap 获取对应的 BeanDefinition

  2. 如果是单例,尝试从缓存中获取,否则创建

  3. 实例化对象(反射)

  4. 注入属性(依赖注入)

  5. 执行初始化方法和 Aware 接口

  6. 返回实例

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 初始化流程:
  1. 解析 XML 根节点元素(如 <context:component-scan>

  2. 查找命名空间 URI

  3. 使用 NamespaceHandlerResolver 查找对应类

  4. 实例化并调用 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" />

解析流程:

  1. 识别命名空间 http://www.springframework.org/schema/context

  2. spring.handlers 找到对应类 ContextNamespaceHandler

  3. ContextNamespaceHandler.init() 注册 ComponentScanBeanDefinitionParser

  4. 解析标签属性 base-package

  5. 使用 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 的生命周期大致如下:

  1. 加载配置(XML、注解等)

  2. 解析为 DOM 或扫描 Class 文件

  3. 生成 BeanDefinition

  4. 注册到 BeanFactory

  5. 容器调用 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-methoddestroy-method 明确指定生命周期钩子:

复制代码
<bean id="example" class="com.Example" init-method="init" destroy-method="cleanup" />
5)关注 Scope 与依赖注入合理性

明确指定 singletonprototype,并确保依赖注入方式与 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 系列容器。

相关推荐
追风少年浪子彦11 分钟前
mybatis-plus实体类主键生成策略
java·数据库·spring·mybatis·mybatis-plus
创码小奇客34 分钟前
Talos 使用全攻略:从基础到高阶,常见问题一网打尽
java·后端·架构
jackzhuoa1 小时前
java小白闯关记第一天(两个数相加)
java·算法·蓝桥杯·期末
Rover.x1 小时前
内存泄漏问题排查
java·linux·服务器·缓存
midsummer_woo1 小时前
基于spring boot的纺织品企业财务管理系统(源码+论文)
java·spring boot·后端
zc-code2 小时前
Spring Boot + @RefreshScope:动态刷新配置的终极指南
java·spring boot·后端
何中应2 小时前
EasyExcel使用(二:写出)
java·后端·maven·excel
minji...2 小时前
数据结构 堆(4)---TOP-K问题
java·数据结构·算法
命苦的孩子3 小时前
Java 中的排序算法详解
java·开发语言·排序算法
java叶新东老师3 小时前
spring gateway 配置http和websocket路由转发规则
spring·http·gateway