Spring IOC 的资源定位

本文深入探讨了Spring框架中IoC容器的启动过程,详细解析了refresh()方法的作用及其实现细节,包括BeanDefinition资源定位和加载的过程。

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

摘要:上一篇博客对spring ioc做了大致概述,大方向上了解了spring ioc的原理,清晰了ioc的设计思路,这篇博客的定位主要是了解ioc如何进行资源定位的加载。
前文回顾:实现了refresh()方法,这个方法非常重要,是ioc的初始化方法,其包含了,获得BeanDefinition的Resource定位,载入和注册。

接下来来分析refresh()方法,并重点了解BeanDefinition的Resource定位,
上源码:AbstractApplicationContext的refresh()方法,顺便打上注释。

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//重点在这
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);  //这是设置spring后置处理
                this.invokeBeanFactoryPostProcessors(beanFactory);   //调用后置处理器
                this.registerBeanPostProcessors(beanFactory);        //注册后置处理器
                this.initMessageSource();                            //初始化上下文
                this.initApplicationEventMulticaster();             //初始化事件
                this.onRefresh();                                   //初始化其他他叔的bean
                this.registerListeners();                           //注册监听器
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

进入obtainFreshBeanFactory()方法

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        this.refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
        }

        return beanFactory;
    }

清晰可见调用了refreshBeanFactory()方法,这里refreshBeanFactory()调用了子类AbstractRefreshableApplicationContext的方法refreshBeanFactory()

这里写图片描述

    protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            this.loadBeanDefinitions(beanFactory);
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

可见,执行refreshBeanFactory()时,判断是否已经存在容器,如果存在,就销毁并且关闭容器,然后根据AbstractRefreshableApplicationContext父类容器创建了DefaultListableBeanFactory()容器。
再调用抽象方法loadBeanDefinitions(beanFactory) 这个方法是重点了,此方法由其子类实现
这里写图片描述
随便看看一个子类的实现,这里我挑了比较常见的子类XmlWebApplicationContext,贴源码

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //这里,我们有看到了上一篇文章的读取器(第三步),可见ioc容器基本离不开那4步,复习
        //1.获得抽象资源,其中包含了BeanDefinition定义的信息。
        //2.定义ioc容器。
        //3.获得BeanDefinition读取器,这里获得抽象资源使用的是xml的方式,通过回调给factory容器
        //4.通过读取器读取资源文件。
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);           
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

对源码解释:beanDefinitionReader.setResourceLoader(this);
设置ResourceLoader,因为XmlWebApplicationContext是ResourceLoader的子类。
疑问: 为啥下面这个方法啥都没干,有没知晓的求告知。

    this.initBeanDefinitionReader(beanDefinitionReader); 
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
    }

大头来了:this.loadBeanDefinitions(beanDefinitionReader);

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            String[] var3 = configLocations;
            int var4 = configLocations.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String configLocation = var3[var5];
                reader.loadBeanDefinitions(configLocation);
            }
        }

    }
    protected String[] getConfigLocations() {
        return this.configLocations != null ? this.configLocations : 
        this.getDefaultConfigLocations();
    } 

这里的getConfigLocations()获取资源文件的路径,调用的是父类的方法,如果构造方法的String… locations 为空,那么就调用其子类的getDefaultConfigLocations()方法。
这里写图片描述
这里我挑选了子类XmlWebApplicationContext.

    protected String[] getDefaultConfigLocations() {
        return this.getNamespace() != null ? new String[]{"/WEB-INF/" + this.getNamespace() + ".xml"} : new String[]{"/WEB-INF/applicationContext.xml"};
    }

可以从代码中看到默认的资源文件路径是/WEB-INF/applicationContext.xml。到了这里终于知道之前写了那么多的applicationContext.xml的用处了。

这里为止,完成了对资源文件的定位,上面那行代码就开始对BeanDefinition的载入工作,将放在下一篇博客继续了解,未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值