Spring IoC容器初始化 — Resource定位源码分析

Spring IoC容器的设计中,有两个主要的容器系列。一个是实现了BeanFactory接口的简单容器系列,这系列容器只实现了容器基本的功能;另外一个是ApplicationContext应用上下文,它在简单容器的基础上增长了许多面向框架的特性,同时对应用环境作了许多适配。java

IoC容器的初始化过程

Spring IoC容器的初始化过程分为三个阶段:Resource定位、BeanDefinition的载入和向IoC容器注册BeanDefinitionSpring把这三个阶段分离,并使用不一样的模块来完成,这样可让用户更加灵活的对这三个阶段进行扩展。数据结构

  • Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader经过统一的Resource接口来完成,Resource对各类形式的BeanDefinition的使用都提供了统一的接口。
  • BeanDefinition的载入是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinitionBeanDefinition实际上就是POJO对象在IoC容器中的抽象。经过BeanDefinitionIoC容器能够方便的对POJO对象进行管理。
  • IoC容器注册BeanDefinition是经过调用BeanDefinitionRegistry接口的实现来完成的,这个注册过程是把载入的BeanDefinitionIoC容器进行注册。实际上,在IoC容器内部维护着一个HashMap,而这个注册过程其实就将BeanDefinition添加至这个HashMap

咱们能够本身定义ResourceBeanFactoryBeanDefinitionReader来初始化一个容器。以下代码片断使用了DefaultListableBeanFactory做为实际使用的IoC容器。同时,建立IoC配置文件(dispatcher-servlet.xml)的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。最后,还须要建立一个载入BeanDefinition的读取器,此处使用XmlBeanDefinitionReader,经过一个回调配置给BeanFactory框架

ClassPathResource res = new ClassPathResource("dispatcher-servlet.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);复制代码

咱们也能够经过ApplicationContext建立一个IoC容器。在Spring中,系统已经提供许多定义好的容器实现,而不须要本身组装。以下代码片断以FileSystemXmlApplicationContext为例建立了一个IoC容器。post

FileSystemXmlApplicationContext context = 
        new FileSystemXmlApplicationContext("classpath:dispatcher-servlet.xml");复制代码

不管使用哪一种方式初始化IoC容器,都会经历上述三个阶段。本篇文章将结合Spring 4.0.2源码,并以FileSystemXmlApplicationContext为例对IoC容器初始化的第一阶段,也就是Resource定位阶段进行分析。this

BeanDefinition的Resource定位

下图展现了FileSystemXmlApplicationContext的继承体系,FileSystemXmlApplicationContext继承自AbstractApplicationContext,而AbstractApplicationContext又继承自DefaultResourceLoaderDefaultResourceLoader实现了ResourceLoader接口。所以FileSystemXmlApplicationContext具有读取定义了BeanDefinitionResource的能力。spa

咱们的分析入口是new FileSystemXmlApplicationContext("classpath:dispatcher-servlet.xml");,这句代码调用了FileSystemXmlApplicationContext的构造方法。FileSystemXmlApplicationContext的构造方法源码以下(只提取与本次分析关联的代码)。debug

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

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if(refresh) {
            this.refresh();
        }
    }复制代码

在建立FileSystemXmlApplicationContext时,咱们仅传入了包含BeanDefinition的配置文件路径(classpath:dispatcher-servlet.xml),由此调用FileSystemXmlApplicationContext(String configLocation)构造方法。接着,FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)构造方法被间接调用,在该构造方法内部,refresh方法完成了整个IoC容器的初始化。所以,refresh方法是咱们分析的下一个入口。设计

refresh方法的具体实现定义在FileSystemXmlApplicationContext的父类AbstractApplicationContext中,对应的源码以下。code

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);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var5) {
                this.destroyBeans();
                this.cancelRefresh(var5);
                throw var5;
            }

        }
    }复制代码

refresh方法中,经过obtainFreshBeanFactory方法,ConfigurableListableBeanFactory类型的BeanFactory被建立。咱们接着进入obtainFreshBeanFactory方法,obtainFreshBeanFactory方法也定义在AbstractApplicationContext中。orm

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

    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;复制代码

咱们重点关注refreshBeanFactory方法的实现。在AbstractApplicationContext中,refreshBeanFactory方法仅仅是个声明,具体的实现委托给了子类完成。此处,refreshBeanFactory方法的具体实现定义在了AbstractRefreshableApplicationContextAbstractRefreshableApplicationContext正是继承自AbstractApplicationContext,这点咱们能够从上文的继承体系图能够得知。refreshBeanFactory方法在AbstractRefreshableApplicationContext中的定义以下。

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

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

    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory var1) throws BeansException, IOException;复制代码

refreshBeanFactory方法首先会判断是否已经创建的BeanFactory,若是已经创建,那么须要销毁并关闭该BeanFactory。接着,refreshBeanFactory方法经过createBeanFactory方法建立了一个IoC容器供ApplicationContext使用,且这个IoC容器的实际类型为DefaultListableBeanFactory。同时,refreshBeanFactory方法将这个IoC容器做为参数,调用loadBeanDefinitions载入了BeanDefinition(本文暂不分析载入过程的具体操做)。

loadBeanDefinitions方法也仅仅在AbstractRefreshableApplicationContext中声明,具体的实现定义在AbstractXmlApplicationContext中,从继承体系图咱们能够得知AbstractXmlApplicationContext正是AbstractRefreshableApplicationContext的子类。loadBeanDefinitions方法对应的源码以下。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        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 {
        Resource[] configResources = this.getConfigResources();
        if(configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if(configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }复制代码

loadBeanDefinitions(DefaultListableBeanFactory beanFactory)中,定义了BeanDefinition的读入器beanDefinitionReaderSpring把定位、读入和注册的过程解耦,这正是体现之处之一。接着beanDefinitionReader做为参数,调用loadBeanDefinitions(XmlBeanDefinitionReader reader)方法,若是configResources为空,那么reader就会根据configLocations调用readerloadBeanDefinitions去加载相应的Resource。在AbstractBeanDefinitionReaderXmlBeanDefinitionReader中个自定义了不一样的loadBeanDefinitions方法,与咱们本次分析相关的代码定义在AbstractBeanDefinitionReader中,以下所示。

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        String[] var3 = locations;
        int var4 = locations.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String location = var3[var5];
            counter += this.loadBeanDefinitions(location);
        }

        return counter;
    }

    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(location, (Set)null);
    }

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = this.getResourceLoader();
        if(resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;
            if(!(resourceLoader instanceof ResourcePatternResolver)) {
                Resource var11 = resourceLoader.getResource(location);
                loadCount = this.loadBeanDefinitions((Resource)var11);
                if(actualResources != null) {
                    actualResources.add(var11);
                }

                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                    Resource[] resource =
                        ((ResourcePatternResolver)resourceLoader).getResources(location);
                    loadCount = this.loadBeanDefinitions(resource);
                    if(actualResources != null) {
                        Resource[] var6 = resource;
                        int var7 = resource.length;

                        for(int var8 = 0; var8 < var7; ++var8) {
                            Resource resource1 = var6[var8];
                            actualResources.add(resource1);
                        }
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }复制代码

loadBeanDefinitions(String location, Set<Resource> actualResources)方法中,咱们能够看到,Resource的定位工做交给了ResourceLoader来完成。对于取得Resource的具体过程,咱们能够看看DefaultResourceLoader是怎样完成的,对应源码以下。

public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if(location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()),
                                         this.getClassLoader());
        } else {
            try {
                URL ex = new URL(location);
                return new UrlResource(ex);
            } catch (MalformedURLException var3) {
                return this.getResourceByPath(location);
            }
        }
    }复制代码

因为咱们传入的locationclasspath:dispatcher-servlet.xml,所以getResource方法会生成一个ClassPathResource并返回,若是咱们传入的是一个文件路径,那么会调用getResourceByPath方法,getResourceByPath方法定义在FileSystemXmlApplicationContext中,对应的源码以下。

protected Resource getResourceByPath(String path) {
        if(path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }复制代码

到此,咱们完成了IoC容器在初始化过程当中的Resource定位过程的流程分析,这为接下来进行BeanDefinition数据的载入和解析创造了条件。

后续我会对BeanDefinition的载入和解析过程结合源码进行分析,欢迎关注。若本文存在分析不妥之处,建议发送邮件至tinylcy (at) gmail.com交流。

相关文章
相关标签/搜索