Spring源码分析-资源定位(一)

 

本blog以FileSystemXmlApplicationContext为例来分析Spring的初始化过程,本节主要介绍资源定位.

1.FileSystemXmlApplicationContext的类继承层次如下图所示

《Spring源码分析-资源定位(一)》

2.类图如下所示(本类图只标示了与本节相关的类、属性和方法)

《Spring源码分析-资源定位(一)》

3.类简介

DefaultResourceLoader: 资源定位类,可以通过一个String类型的path获取一个Resource,从而指向一个具体的文件。

                                              此类中最重要的方法是Resource getResource(String location),该方法间接调用getResourceByPath来获取Resource.

                                              FileSystemXmlApplicationContext覆写了父类DefaultResourceLoader中的getResourceByPath方法。

AbstractApplicationContext:refresh方法是入口方法也是核心方法,定义了生成ApplicationContext的实现步骤,使用了典型的模板方法模式,一些子步骤交给子类来实现。

                                                  obtainFreshBeanFactory方法可以获取一个真正的底层beanFactory,其中的refreshBeanFactory()和getBeanFactory()都是抽象方法

AbstractRefreshableConfigApplication: refreshBeanFactory是对父类AbstractApplicationContext中refreshBeanFactory的实现。

                                                                        createBeanFactory()是真正的创建工厂的方法,在此方法中创建了一个DefaultListableBeanFactory类型的工厂。

                                                                        此类的中的beanFactory属性保存了创建的工厂句柄。

AbstractRefreshableConfigApplicationContext:这个类的最主要的作用的保存配置文件的信息,主要存储在configLocations数组中。

                                                                                     此类提供了setConfigLocations和getConfigLocations方法。

AbstractXmlApplicationContext:最核心的方法是loadBeanDefinitions(DefaultListableBeanFactory beanFactory),此方法是对父类 AbstractRefreshableApplicationContext中方法的覆盖,此方法拉开了解析XML配置文件的序幕。

FileSystemXmlApplicationContext:最底层的构造方法 FileSystemXmlApplicationContext(String configLocations[], boolean refresh, ApplicationContext parent)

                                                               getResourceByPath方法是对DefaultResourceLoader中getResourceByPath的覆盖。

4.源码解析

(一)构造一个FileSystemXmlApplicationContext的工厂,最终会调用的构造方法如下:

    public FileSystemXmlApplicationContext(String configLocations[], boolean refresh, ApplicationContext parent)
        throws BeansException
    {
        super(parent);
        setConfigLocations(configLocations);
        if(refresh)
            refresh();
    }

setConfigLocations:实际是调用从父类(AbstractRefreshableConfigApplicationContext)继承下来的方法,目的是把值保存在从父类(AbstractRefreshableConfigApplicationContext)继承下来的属性configLocations中。

refresh是核心方法,启动了资源定位、解析等一系统的后续过程。此refresh方法实际上调用从父类(AbstractApplicationContext)继承下来的refresh方法.

(二)refresh方法解析(AbstractApplicationContext)

    public void refresh()
        throws BeansException, IllegalStateException
    {
        synchronized(startupShutdownMonitor)
        {
            prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            try
            {
                postProcessBeanFactory(beanFactory);
                invokeBeanFactoryPostProcessors(beanFactory);
                registerBeanPostProcessors(beanFactory);
                initMessageSource();
                initApplicationEventMulticaster();
                onRefresh();
                registerListeners();
                finishBeanFactoryInitialization(beanFactory);
                finishRefresh();
            }
            catch(BeansException ex)
            {
                beanFactory.destroySingletons();
                cancelRefresh(ex);
                throw ex;
            }
        }
    }

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory()
    {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if(logger.isInfoEnabled())
            logger.info("Bean factory for application context [" + getId() + "]: " + ObjectUtils.identityToString(beanFactory));
        if(logger.isDebugEnabled())
            logger.debug(beanFactory.getBeanDefinitionCount() + " beans defined in " + this);
        return beanFactory;
    }

refreshBeanFactory是核心方法,此方法的实现交由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来实现。

(三)refreshBeanFactory方法解析(AbstractRefreshableApplicationContext)

    protected final void refreshBeanFactory()
        throws BeansException
    {
        if(hasBeanFactory())
        {
            destroyBeans();
            closeBeanFactory();
        }
        try
        {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized(beanFactoryMonitor)
            {
                this.beanFactory = beanFactory;
            }
        }
        catch(IOException ex)
        {
            throw new ApplicationContextException("I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
        }
    }

这个方法中最重要的方法是createBeanFactory()方法,这个方法是真正的创建工厂的方法,创建后的工厂保存在beanFactory属性中。

    protected DefaultListableBeanFactory createBeanFactory()
    {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

refreshBeanFactory方法的loadBeanDefinitions(beanFactory);方法启动了bean的注册过程.loadBeanDefinitions(DefaultListableBeanFactory defaultlistablebeanfactory)的实现交给子类AbstractXmlApplicationContext来完成.

(四)loadBeanDefinitions源码解析(AbstractXmlApplicationContext)

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
        throws IOException
    {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

在loadBeanDefinitions方法中,生成了XmlBeanDefinitionReader,实际上bean的解析工作交给了beanDefinitionReader来处理.

在loadBeanDefinitions(DefaultListableBeanFactory beanFactory)中调用了重载的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法,源码如下

(五)loadBeanDefinitions(XmlBeanDefinitionReader reader)源码解析(AbstractXmlApplicationContext)

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)
        throws BeansException, IOException
    {
        Resource configResources[] = getConfigResources();
        if(configResources != null)
            reader.loadBeanDefinitions(configResources);
        String configLocations[] = getConfigLocations();
        if(configLocations != null)
            reader.loadBeanDefinitions(configLocations);
    }

在本方法中调用的getConfigLocations实际上调用的父类(AbstractRefreshableConfigApplicationContext)继承下来的方法,configLocations[]的值实际上是在(一)调用FileSystemXmlApplicationContext的构造方法时设置进去的( setConfigLocations(configLocations)).在这个方法中,我们可以看reader.loadBeanDefinitions(configLocations);启动了真正的解析动作.

(六)public int loadBeanDefinitions(String locations[])源码解析(AbstractBeanDefinitionReader)

(AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类)

 public int loadBeanDefinitions(String location, Set actualResources)
        throws BeanDefinitionStoreException
    {
        ResourceLoader resourceLoader = getResourceLoader();
        if(resourceLoader == null)
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        int loadCount;
        if(resourceLoader instanceof ResourcePatternResolver)
            try
            {
                Resource resources[] = ((ResourcePatternResolver)resourceLoader).getResources(location);
                loadCount = loadBeanDefinitions(resources);
                if(actualResources != null)
                {
                    for(int i = 0; i < resources.length; i++)
                        actualResources.add(resources[i]);

                }
                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);
            }
        Resource resource = resourceLoader.getResource(location);
        loadCount = loadBeanDefinitions(resource);
        if(actualResources != null)
            actualResources.add(resource);
        if(logger.isDebugEnabled())
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
        return loadCount;
    }

getResourceLoader()getResourceLoader()getResourceLoader()getResourceLoader()getResourceLoader()实际上获取的是new的工厂自身,因为在源码(四)中XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setResourceLoader(this);工厂本身也实现了DefaultResourceLoader.

Resource resource = resourceLoader.getResource(location);是获取转换后的资源,实际上间接调用了FileSystemXmlApplicationContext.getResourceByPath方法,这个方法会把一个String转化为一个Resource。

loadBeanDefinitions(resource)则结束了本节的讨论,转入了下一篇blog.

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qian_348840260/article/details/7971956
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞