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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

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

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

20191102100500\_1.png

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

20191102100500\_2.png

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.


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析-资源定位(一)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏