逐行解读Spring(一) - Xml配置解析,bean标签的解析

本文从源码角度逐行解读Spring XML配置文件中bean标签的解析过程。首先介绍背景,强调从基础开始学习Spring源码的重要性。接着通过一个简单的bean配置示例,逐步解析beanDefinition的注册流程,详细探讨bean标签的属性和子标签功能。最后总结bean标签解析的核心步骤,为后续bean实例创建奠定基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

最近在看spring源码,发现之前看的很多细节已经忘了,于是决定在看源码的过程中也把主要的流程用博客记载下来,希望自己能坚持下来吧。

spring已经发展很久,整个体系已经变得很庞大了。为了能更好的把源码看下去,我决定从最基础也是最核心的IOC开始切入,并且从最原始的xml解析开始看。面对这样一个庞大的体系,我认为从最原始的方式开始学习,才能更好的看懂它的设计和实现思路。

这一系列文章会默认你对于spring的使用已经熟悉,并且不抗拒读源码。因为很多的文字会在源码片段上注释-对于源码解析的文章,我暂时也找不到更好的表述方法了。

二、一个简单的示例

首先我们配置一个bean

<bean class="com.xiaoxizi.spring.service.AccountServiceImpl" 
      id="accountService" scope="singleton" primary="true"/>

对应的类:

public class AccountServiceImpl implements AccountService {
   
    @Override
    public String queryAccount(String id) {
   
        return null;
    }
}

测试类:

@Test
public void test1() {
   
    applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AccountService bean = applicationContext.getBean(AccountService.class);
    System.out.println(bean);
}

运行结果:

com.xiaoxizi.spring.service.AccountServiceImpl@3e78b6a5

三、源码解析

1. beanDefinition注册流程

我们知道,spring容器启动的逻辑在refresh()方法里面。所以,话不多说,直接点进refresh()逻辑,具体位置是 org.springframework.context.support.AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {
   
    synchronized (this.startupShutdownMonitor) {
   
        prepareRefresh();
        // 本篇博文主要讲这个逻辑
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        ...
}

本篇博文主要讲xml解析的逻辑,暂时我们只关注 obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   
    refreshBeanFactory();
    return getBeanFactory();
}

继续往下跟refreshBeanFactory(),实际上方法位置在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

@Override
protected final void refreshBeanFactory() throws BeansException {
   
    if (hasBeanFactory()) {
   
        destroyBeans();
        closeBeanFactory();
    }
    try {
   
        // 创建一个beanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        // 加载所有的BeanDefinitions,实际解析xml的位置
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
   
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
   
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

继续往下 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   
    // 这里使用了委托模式,把BeanDefinition的解析委托给了 BeanDefinitionReader
    // 由于我们当前是解析xml,所以是委托给Xml...Reader。合理想象,注解方式将会委托给Anno...Reader
    // 需要注意的是,我们把beanFactory引用传递给了Reader,之后会用到
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	// ... 为BeanDefinitionReader设置了一些不重要的属性,略过。
    // 加载BeanDefinition
    loadBeanDefinitions(beanDefinitionReader);
}
// XmlBeanDefinitionReader对应构造器,注意 beanFactory 是作为 BeanDefinitionRegistry 传入的
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
   
    super(registry);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
   
        // 可以看到,是委托给Reader来加载BeanDefinition的
        reader.loadBeanDefinitions(configResources);
    }
    // 配置文件位置实际上就是我们 new ClassPathXmlApplicationContext("xxx") 时传入的
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
   
        // 可以看到,是委托给Reader来加载BeanDefinition的
        reader.loadBeanDefinitions(configLocations);
    }
}

经过一系列解析、包装、加载逻辑之后… org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
   

    try {
   
        // 配置文件的输入流被加载成了Document --> XML解析知识,详细可搜素 SAX解析
        Document doc = doLoadDocument(inputSource, resource);
        // 解析并注册BeanDefinitions
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
   
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
	// 异常处理,省略...
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   
    // 又来了,熟悉的委托模式,Document的解析被委托给了BeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值