spring ioc 的资源定位 载入和注册

本文以FileSystemXmlApplicationContext为例,探讨Spring IOC的资源定位和注册过程。首先,通过调用父类构造函数设置Bean资源加载器,然后设置Bean定义资源文件的定位路径。接着,深入分析了Spring的继承体系,解释了ResourceLoader如何在不同类中实现。最后,介绍了刷新容器的过程,特别是从refreshBeanFactory()方法开始的Bean定义资源文件载入。
摘要由CSDN通过智能技术生成

Spring ioc的资源定位 载入和注册

以FileSystemXmlApplicationContext为例

package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
    public FileSystemXmlApplicationContext() {
    }
public FileSystemXmlApplicationContext(ApplicationContext parent) {
    super(parent);
}

public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
    this(new String[]{configLocation}, true, (ApplicationContext)null);
}

public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
    this(configLocations, true, (ApplicationContext)null);
}

public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
    this(configLocations, true, parent);
}

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
    this(configLocations, refresh, (ApplicationContext)null);
}
--核心的构造方法基本其他的构造方法都是通过调用此方法进行的--
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
    super(parent);
    this.setConfigLocations(configLocations);
    if(refresh) {
        this.refresh();
    }

}

protected Resource getResourceByPath(String path) {
    if(path != null && path.startsWith("/")) {
        path = path.substring(1);
    }

    return new FileSystemResource(path);
}

}


在核心的构造函数中发现调用了
- super(parent)
- this.setConfigLocations(configLocations);
- this.refresh()
理解为调用父类容器的构造方法(super(parent)方法)为容器设置好Bean资源加载器。
然后,再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。
通过追踪FileSystemXmlApplicationContext的继承体系。
1. ### 先看super(parent)
首先调用了父类AbstractXmlApplicationContext的构造

public AbstractXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

继续

public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) {
        super(parent);
    }

再继续

public AbstractApplicationContext(ApplicationContext parent) {

        this();
        this.setParent(parent);
    }

this();的具体构造如下.

public AbstractApplicationContext() {  
        this.logger = LogFactory.getLog(this.getClass());  
        this.id = ObjectUtils.identityToString(this);  
        this.displayName = ObjectUtils.identityToString(this);  
        this.beanFactoryPostProcessors = new ArrayList();  
        this.active = new AtomicBoolean();  
        this.closed = new AtomicBoolean();  
        this.startupShutdownMonitor = new Object();  
        this.applicationListeners = new LinkedHashSet();  
        this.resourcePatternResolver = this.getResourcePatternResolver();
    }


    从this.resourcePatternResolver =this.getResourcePatternResolver();跟进去会发现


protected ResourcePatternResolver getResourcePatternResolver() {
        return new PathMatchingResourcePatternResolver(this);  
    }


public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");  
         //设置Spring的资源加载器   
        this.resourceLoader = resourceLoader;  
    }

FileSystemXmlApplicationContext的继承体系如下:

这里写图片描述
- 由此继承体系可知道FileSystemXmlApplication的基类接口就是一个ResourceLoader而实现这个接口的类是DefautResourceLoader,所以默认的资源加载器会是DefautResourceLoader
- 由源码发现FileSystemXmlApplication通过调用父类AbstractXmlApplicationContext构造方法,该类又通过调用其父类AbstractRefreshableConfigApplicationContext的构造方法,AbstractRefreshableConfigApplicationContext类继续调用父类直到AbstractApplicationContext类后调用其默认构造方法,通过该类的getResourcePatternResolver()return new PathMatchingResourcePatternResolver返回一个ResourcePatternResolver实例有源码


public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String var1) throws IOException;
}  
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {.....}


由源码知道 ResourcePatternResolver、AbstractApplicationContext都继承了 ResourceLoader, PathMatchingResourcePatternResolver实现ResourcePatternResolver 所以AbstractApplicationContext、PathMatchingResourcePatternResolver都从而实现了spring中ResourceLoader的设置,这里也就仅仅是设置而已。
2. ## 再看this.setConfigLocations(configLocations)

/即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式  
public void setConfigLocation(String location) {
        this.setConfigLocations(StringUtils.tokenizeToStringArray(location, ",; \t\n"));
    }   
    //解析Bean定义资源文件的路径,处理多个资源文件字符串数组
public void setConfigLocations(String... locations) {
        if(locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];

            for(int i = 0; i < locations.length; ++i) {
            // resolvePath为同一个类中将字符串解析为路径的方法 实际就是字符串
                this.configLocations[i] = this.resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }
    }

     protected String resolvePath(String path) {
        return this.getEnvironment().resolveRequiredPlaceholders(path);
    }
    至此,Spring IoC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource。
  1. ## 最后 this.refresh()
    refresh()在基类AbstractApplicationContext中
public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();  
            //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动  
            采用委派的方式即在类中只有一个abstract方法由子类实现。 
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);
            <---这些方法先不管-->

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                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();
            }

        }
    }

Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中

ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();

这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法  
,具体实现调用子类容器的refreshBeanFactory()方法
        this.refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
        }

        return beanFactory;
    }

子类AbstractRefreshableApplicationContext实现refreshBeanFactory()

protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,销毁容器中的bean,关闭容器
        if(this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
        <!--创建ioc容器--->
            DefaultListableBeanFactory ex = this.createBeanFactory();
            ex.setSerializationId(this.getId());
            <!--对容器进行制定只要是true flase的设置-->
            this.customizeBeanFactory(ex);
            //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前  
            类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器  
            this.loadBeanDefinitions(ex);
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = ex;
            }

看子类AbstractXmlApplicationContext中的loadBeanDefinitions方法

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
<!--创建bean资源读取器-->
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        <!--对reader进行一系列的设置
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        <!加载的地方
        this.loadBeanDefinitions(beanDefinitionReader);
    }  

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    /获取Bean定义资源的定位 //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法  
   //该方法在ClassPathXmlApplicationContext中进行实现,对于我们  
    //举例分析源码的FileSystemXmlApplicationContext没有使用该方法  
        Resource[] configResources = this.getConfigResources();
        if(configResources != null) {
        //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位  
        //的Bean定义资源  

            reader.loadBeanDefinitions(configResources);
        }
    如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
        String[] configLocations = this.getConfigLocations();
        if(configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

不是很到位的理解一下:

首先从FileSystemXmlApplicationContext这个类的refresh()启动,基类AbstractApplicationContext实现的在这个方法里同时调用了obtainFreshBeanFactory()而在    
obtainFreshBeanFactory()里通过调用refreshBeanFactory()创建IoC容器、,并调用loadBeanDefinitions(beanFactory)装载bean,这两个方法都是通过委派实现的。  
loadBeanDefinitions(beanFactory)中通过调用loadBeanDefinitions(beanDefinitionReader)实现bean加载,在loadBeanDefinitions(beanDefinitionReader)里Bean   
读取器(reader)调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资(这里用了两种方式一个是直接的url定位读取,另一种是在resource里面读取)  
这里委派调用其子类XmlBeanDefinitionReader 的方法loadBeanDefinitions(resource),实现加载功能。在loadBeanDefinitions(resource)中调   
用loadBeanDefinitions(EncodedResource encodedResource)载入XML形式Bean定义资源文件具体读取过程的方法doLoadBeanDefinitions(),在该   
方法中调用documentLoader.loadDocument(..)把XML文件转换为DOc对象接着调用registerBeanDefinitions(Document doc, Resource resource)  
按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构,该方法里面调用了registerBeanDefinitions(Document doc, XmlReaderContext  
readerContext)委派子类--根据  
Spring DTD对Bean的定义规则解析Bean定义Document对象里面调用很多方法解析xml文件中所用标签例如:
</bean>等。最后BeanDefinitionReaderUtils.registerBeanDefinition进入注册,当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正  
完成注册功能的是DefaultListableBeanFactory.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值