当前位置:首页 > 技术 > 正文内容

深度解析Spring IOC容器工作机制

访客 技术 2026年7月7日 1

控制反转(IOC,Inversion of Control)是Spring框架的核心设计理念,它深刻影响了Java企业级应用的开发方式。

IOC与DI的概念解析

控制权的转移:传统开发模式下,对象通过new关键字主动创建依赖对象,这种方式导致对象之间高度耦合。IOC容器接管了对象的创建权,控制权从应用代码转移到容器。

依赖注入的实现:依赖注入(DI,Dependency Injection)是IOC思想的具体实现方式。容器在运行时动态地将依赖对象注入到目标对象中,实现解耦。

IOC与DI关系图

在Spring框架中,由IOC容器管理的对象称为Spring Bean

Spring IOC容器类型

Spring框架提供了两种类型的IOC容器:

  • BeanFactory:基础容器接口,提供配置框架和基本功能,支持管理任意类型的对象
  • ApplicationContext:BeanFactory的子接口,在基础上增强了消息资源处理(国际化支持)、事件发布机制、资源访问等企业级功能

容器继承关系

IOC容器初始化流程

以XML配置文件为例,分析Spring IOC容器的初始化过程。定义一个简单的Bean配置:

<?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="dataService" class="com.example.service.DataService" scope="singleton">
    </bean>

</beans>

启动容器的典型代码:

public class ApplicationBootstrap {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("dataService.xml");
        DataService service = ctx.getBean("dataService", DataService.class);
        service.process();
    }
}

通过ClassPathXmlApplicationContext构造方法创建容器,其核心逻辑如下:

public ClassPathXmlApplicationContext(
        String[] configPaths, boolean shouldRefresh, ApplicationContext parentCtx) 
        throws BeansException {
    
    super(parentCtx);
    setConfigLocations(configPaths);
    if (shouldRefresh) {
        refresh();
    }
}

核心方法refresh()完成了容器的完整初始化:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this startupMonitor) {
        StartupStep refreshStep = this.appStartup.start("spring.context.refresh");
        
        // 第一步:准备容器刷新
        prepareRefresh();
        
        // 第二步:获取新鲜的BeanFactory
        ConfigurableListableBeanFactory factory = obtainFreshBeanFactory();
        
        // 第三步:配置BeanFactory
        prepareBeanFactory(factory);
        
        try {
            // 第四步:子类扩展处理
            postProcessBeanFactory(factory);
            
            StartupStep postProcessStep = this.appStartup.start("spring.context.beans.post-process");
            
            // 第五步:执行BeanFactory后置处理器
            invokeBeanFactoryPostProcessors(factory);
            
            // 第六步:注册Bean后置处理器
            registerBeanPostProcessors(factory);
            postProcessStep.end();
            
            // 第七步:初始化消息源
            initMessageSource();
            
            // 第八步:初始化事件广播器
            initApplicationEventMulticaster();
            
            // 第九步:刷新特定Bean
            onRefresh();
            
            // 第十步:注册监听器
            registerListeners();
            
            // 第十一步:实例化非延迟加载的Bean
            finishBeanFactoryInitialization(factory);
            
            // 第十二步:完成刷新
            finishRefresh();
        }
        catch (BeansException e) {
            destroyBeans();
            cancelRefresh(e);
            throw e;
        }
        finally {
            resetCommonCaches();
            refreshStep.end();
        }
    }
}

BeanFactory初始化详解

obtainFreshBeanFactory()方法负责BeanFactory的创建:

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

@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory factory = createBeanFactory();
        factory.setSerializationId(getId());
        customizeBeanFactory(factory);
        loadBeanDefinitions(factory);
        this.beanFactory = factory;
    }
    catch (IOException e) {
        throw new ApplicationContextException("解析Bean定义文件失败", e);
    }
}

资源定位与加载

XmlBeanDefinitionReader负责加载XML配置:

protected void loadBeanDefinitions(DefaultListableBeanFactory factory) 
        throws BeansException, IOException {
    
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    reader.setEnvironment(getEnvironment());
    reader.setResourceLoader(this);
    reader.setEntityResolver(new ResourceEntityResolver(this));
    initBeanDefinitionReader(reader);
    loadBeanDefinitions(reader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) 
        throws BeansException, IOException {
    
    Resource[] resources = getConfigResources();
    if (resources != null) {
        reader.loadBeanDefinitions(resources);
    }
    String[] locations = getConfigLocations();
    if (locations != null) {
        reader.loadBeanDefinitions(locations);
    }
}

Bean定义解析与注册

解析XML并生成Document对象后,BeanDefinitionDocumentReader负责解析:

public int registerBeanDefinitions(Document doc, Resource resource) 
        throws BeanDefinitionStoreException {
    
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

protected void doRegisterBeanDefinitions(Element root) {
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
}

protected void processBeanDefinition(Element element, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder holder = delegate.parseBeanDefinitionElement(element);
    if (holder != null) {
        holder = delegate.decorateBeanDefinitionIfRequired(element, holder);
        try {
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException e) {
            getReaderContext().error("注册Bean定义失败: " + holder.getBeanName(), element, e);
        }
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(holder));
    }
}

最终通过DefaultListableBeanFactory的registerBeanDefinition方法将BeanDefinition保存到容器中:

public void registerBeanDefinition(String beanName, BeanDefinition bd) 
        throws BeanDefinitionStoreException {
    
    Assert.hasText(beanName, "Bean名称不能为空");
    Assert.notNull(bd, "BeanDefinition不能为空");
    
    if (bd instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) bd).validate();
        }
        catch (BeanDefinitionValidationException e) {
            throw new BeanDefinitionStoreException("Bean定义校验失败", e);
        }
    }
    
    BeanDefinition existingBd = this.beanDefinitionMap.get(beanName);
    if (existingBd != null) {
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, bd, existingBd);
        }
        this.beanDefinitionMap.put(beanName, bd);
    }
    else {
        if (hasBeanCreationStarted()) {
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, bd);
                this.beanDefinitionNames.add(beanName);
            }
        }
        else {
            this.beanDefinitionMap.put(beanName, bd);
            this.beanDefinitionNames.add(beanName);
        }
    }
    
    if (existingBd != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

IOC容器初始化总结

Spring IOC容器初始化包含三个核心环节:

  1. 资源定位:通过ResourceLoader定位配置文件
  2. Bean定义加载:通过BeanDefinitionReader解析资源生成BeanDefinition
  3. Bean注册:通过BeanDefinitionRegistry将BeanDefinition存入ConcurrentHashMap

IOC容器通过ConcurrentHashMap保存所有BeanDefinition信息,实现了Bean对象的统一管理,有效降低了应用组件之间的耦合度。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。