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

来源:互联网 发布:python发送邮件带附件 编辑:程序博客网 时间:2024/06/13 04:37

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.
阅读全文
0 0
原创粉丝点击