IOC容器初始化——BeanDefinition的Resource定位

以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这是使用的是ClassPathResource,意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition的信息。

ClassPathResource res =new ClassPathResource(‘beans.xml‘);

这里定义的Resource不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。在ApplicationContext中,提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IOC容器,需要为它配置特定的读取器来完成功能。但是使用DefaultListableBeanFactory这种底层的容器,能提高IOC容器的灵活性。

我们经常使用的ApplicationContext,比如FileSystemXmlApplicationContext、ClassPathXmlSystemXmlApplicationContext以及XmlWebApplicationContext。从类的名字可以看出它们提供哪些不同的Resource读入功能,依次比如为从文件系统,从class path,从web容器载入Resource等。

我们以FileSystemXmlApplicationContext为例,继承关系如下图:

因为基类是DefaultResourceLoader(它实现了ResourceLoader接口),这个FileSystemXmlApplicationContext已经具备ResourceLoader的读入功能。

先看下FileSystemXmlApplicationContext的实现:

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

    /**
     * Create a new FileSystemXmlApplicationContext for bean-style configuration.
     * @see #setConfigLocation
     * @see #setConfigLocations
     * @see #afterPropertiesSet()
     */
    public FileSystemXmlApplicationContext() {
    }

    /**
     * Create a new FileSystemXmlApplicationContext for bean-style configuration.
     * @param parent the parent context
     * @see #setConfigLocation
     * @see #setConfigLocations
     * @see #afterPropertiesSet()
     */
    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML file and automatically refreshing the context.
     * @param configLocation file path
     * @throws BeansException if context creation failed
     */
    //这个构造函数的configLocation包含的是BeanDefinition所在的文件路径
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML files and automatically refreshing the context.
     * @param configLocations array of file paths
     * @throws BeansException if context creation failed
     */
    //这个构造函数的configLocation包含多个BeanDefinition的文件路径
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files and automatically
     * refreshing the context.
     * @param configLocations array of file paths
     * @param parent the parent context
     * @throws BeansException if context creation failed
     */
    //这个构造函数的configLocation包含的多个BeanDefinition文件路径的同时,还允许指定自己的双亲IOC容器
    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML files.
     * @param configLocations array of file paths
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files.
     * @param configLocations array of file paths
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    //在对象的初始化过程中,调用refresh方法载入BeanDefinition,这个refresh方法启动了BeanDefinition的载入过程,待会看refresh详细分析
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

    /**
     * Resolve resource paths as file system paths.
     * <p>Note: Even if a given path starts with a slash, it will get
     * interpreted as relative to the current VM working directory.
     * This is consistent with the semantics in a Servlet container.
     * @param path path to the resource
     * @return Resource handle
     * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
     */
    @Override
    //这是应用于文件系统中的Resource的实现,通过构造一个FileSystemResource来得到一个文件在
    //系统中定位的BeanDefinition
    //这个getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被调用
    //loadBeanDefinition采用模板模式,具体的定位实现是由各个子类来完成
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

refresh方法非常重要,容器初始化过程的一个重要入口。refresh()在AbstractApplicationContext实现,看下部分代码:

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //子类中启动refreshBeanFactory的地方
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //设置beanFactory的后置处理
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //调用beanFactory的后置处理器,这些后处理器在bean中向容器注册的
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //注册bean的后处理器,在bean创建过程中调用
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //对上下文中的消息源进行初始化
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化上下文中的事件机制
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //初始化其中的特殊bean
                onRefresh();

                // Check for listener beans and register them.
                //检查监听bean并且将这些bean向容器注册
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //实例化所有non-lazy-init单件
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //发布容器事件,结束refresh过程
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                //为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
                destroyBeans();

                // Reset ‘active‘ flag.
                //重置active标志
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

在IOC容器的初始化过程中,BeanDefinition的定位,读入和载入过程是分开进行的,这是解耦的一个体现。关于读入器的配置,要先看下FileSystemXmlApplicationContext的基类AbstractRefreshableApplicationContext实现。需要重点看你下AbstractRefreshableApplicationContext的refreshBeanFactory方法,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh方法调用(refresh方法调用了obtainFreshBeanFactory()上面代码可以看出)。

下面看下AbstractRefreshableApplicationContext对容器初始化的代码清单

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建ioc容器,DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            //启动对BeanDefinition的载入
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

在这个方法中,通过createBeanFactory方法构建了一个IOC容器供Application使用。这个IOC容器就是前面提到的DefaultListableBeanFactory,同时它启动了loadBeanDefinitions来载入BeanDefinition。

//这就是在上下文中创建DefaultListableBeanFactory的地方,getInternalParentBeanFactory()的具体实现在
    //AbstractApplicationContext,会根据已有的双亲IOC容器信息来完成DefaultListableBeanFactory的双亲IOC容器

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
    /**
     *
     *     protected BeanFactory getInternalParentBeanFactory() {
              return (getParent() instanceof ConfigurableApplicationContext) ?
                     ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
    }
     */
/**
     * Load bean definitions into the given bean factory, typically through
     * delegating to one or more bean definition readers.
     * @param beanFactory the bean factory to load bean definitions into
     * @throws BeansException if parsing of the bean definitions failed
     * @throws IOException if loading of bean definition files failed
     * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     */
    //这里是使用 BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式,虽然用的最多的是XML定义的形式,
    //这里通过一个抽象函数把具体的实现委托给子类完成
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //取得ResourceLoader使用的是DefaultResourceLoader
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        //对Resource路径解析,Resource集合可以是多个文件
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                //取得具体的Resource定位
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

对于取得Resource的过程,看下DefaultResourceLoader是怎么完成的

public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith(CLASSPATH_URL_PREFIX)) { //     public static final String CLASSPATH_URL_PREFIX = "classpath:";
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {
            try {
                // Try to parse the location as a URL...
                //这里处理URL标识的Resource定位
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                //如果既不是classpath,也不是URL标识的Resource定位,则把getResource交给getResourceByPath,
                //这个方法是一个protected方法,默认实现是得到一个ClassPathContextResource,这个方法会用子类实现
                return getResourceByPath(location);
            }
        }
    }
    protected Resource getResourceByPath(String path) {
        return new ClassPathContextResource(path, getClassLoader());
    }

对于上面的FileSystemXmlApplicationContext对getResourceByPath方法的实现就是下面方法

    @Override
    //这是应用于文件系统中的Resource的实现,通过构造一个FileSystemResource来得到一个文件在
    //系统中定位的BeanDefinition
    //这个getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被调用
    //loadBeanDefinition采用模板模式,具体的定位实现是由各个子类来完成
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

返回的FileSystemResource对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。

如果是其他的ApplicationContext,那么会对应生成其他种类的Resource。比如ClassPathResource、ServletContextResource等。关于Resource种类,可以看下继承关系图

作为接口的Resource定义了许多与I/O相关的操作。

通过前面的实现原理的分析,我们以FileSystemXmlApplicationContext的实现原理为例,了解了Resource的定位问题,既是FileSystem方式存在的Resource的定位实现。在在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象来进行载入了。在定位过程完成以后,为BeanDefinition的载入创造了I/O操作的条件,但是具体的数据还没有开始读入。

时间: 2024-10-15 00:46:18

IOC容器初始化——BeanDefinition的Resource定位的相关文章

IOC容器初始化

参考:https://www.cnblogs.com/ITtangtang/p/3978349.html 一.Ioc/DI 控制反转,把原先代码里需要实现的对象的创建.依赖的代交给容器帮忙实现.所以需要创建一个容器,同时需要一种描述让容器知道需要创建的对象与对象之间的关系. 对象与对象之间的关系通过 xml, properties 文件等语义化配置文件表示.文件存放位置:类路径,文件系统,URL,servletContext等. 不同的配置文件对对象的描述不一样,所以内部需要一个统一的关于对象的

Spring IoC容器初始化过程

IoC容器是什么?IoC文英全称Inversion of Control,即控制反转,我么可以这么理解IoC容器: 把某些业务对象的的控制权交给一个平台或者框架来同一管理,这个同一管理的平台可以称为IoC容器. 我们刚开始学习spring的时候会经常看到的类似下面的这代码: ApplicationContext appContext = new ClassPathXmlApplicationContext("cjj/models/beans.xml"); Person p = (Per

springmvc web.xml配置之SpringMVC IOC容器初始化

SpringMVC IOC容器初始化 首先强调一下SpringMVC IOC容器初始化有些特别,在SpringMVC中除了生成一个全局的spring Ioc容器外,还会为DispatcherServlet生成一个容器,具体的下一篇有讲述. 我们知道spring中单纯使用main函数就可以生成一个容器,如下: public class MainTest { public static void main(String[] args){ ApplicationContext appContext =

【spring源码分析】IOC容器初始化(一)

前言:spring主要就是对bean进行管理,因此IOC容器的初始化过程非常重要,搞清楚其原理不管在实际生产或面试过程中都十分的有用.在[spring源码分析]准备工作中已经搭建好spring的环境,并利用xml配置形式对类进行了实例化.在test代码中有一个非常关键的类ClassPathXmlApplicationContext,在这个类中实现了IOC容器的初始化,因此我们从ClassPathXmlApplicationContext入手开始研究IOC的初始化过程. 1.ClassPathXm

【spring源码分析】IOC容器初始化(总结)

前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正的bean对象. 总结 [spring源码分析]IOC容器初始化(一):主要分析了Spring是如何解析占位符以及BeanFactory的最终实现类DefaultListableBeanFactory. [spring源码分析]IOC容器初始化(二):以loadBeanDefinitions函数为切

WebApi 插件式构建方案:IOC 容器初始化

body { border: 1px solid #ddd; outline: 1300px solid #fff; margin: 16px auto; } body .markdown-body { padding: 30px; } @font-face { font-family: octicons-anchor; src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAA

BeanDefinition的Resource定位——3

1.我们重点看看AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用.在这个方法中,通过createBeanFactory构建了一个IoC容器供ApplicationContext使用.这个IoC容器就是我们前面提到过的DefaultListableBeanFactory,同时,它启动了loa

BeanDefinition的Resource定位——2

1.FileSystemXmlApplicationContext的实现 1 public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { 2 3 /** 4 * Create a new FileSystemXmlApplicationContext for bean-style configuration. 5 * @see #setConfigLocation 6 * @see #s

Spring之IOC容器初始化过程

Ioc容器的初始化是由refresh()方法来启动的,这个方法标志着Ioc容器的正式启动. 具体来说这个启动过程包括三个基本过程: 1.BeanDifinition的Resource定位 2.BeanDifinition的载入与解析 3.BeanDifinition在Ioc容器中的注册 需要注意的是,Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的ResourceLoader.BeanDifinitionReader等模块,通过这样的实际方式,可以让用户更加灵活的对这三个过程进