• 【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory


    obtainFreshBeanFactory()方法概述

    定义BeanFactory,并加载以下两种bean的定义,装配到BeanFactory:

    1.配置文件中定义的bean

    2.通过<context:component-scan base-package="..." />配置的路径下的,且经过相应注解标注的所有类,注解包括:@Controller、@Service、@Component、@Repository

    源码解读

    主要流程总结:

    1.创建BeanFactory:DefaultListableBeanFactory

    2.解析web.xml配置,读取spring配置文件,封装为Resource对象

    3.把Resource对象封装为Document对象

    4.开始层层遍历Document的节点。

    以下是细节:

    先来看该方法的实现,注:这里会把无关代码删掉,以方便阅读。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            //刷新bean工厂
    
         this.refreshBeanFactory();
        //创建bean工厂
        return this.getBeanFactory();
     } 

    重点看刷新bean工厂部分:

    protected final void refreshBeanFactory() throws BeansException {
           //创建bean工厂
                DefaultListableBeanFactory beanFactory = createBeanFactory();
            //这里加载beanDefinition,并赋给bean工厂          
           loadBeanDefinitions(beanFactory);
        }
    createBeanFactory()好理解,就是new了个工厂对象。
    有了工厂对象后,就需要往里面装载东西,装什么呢?这里是

    接下来看loadBeanDefinitions(beanFactory)方法的具体实现:创建xml文件读取器

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // 以下这一堆内容就是为了准备一个xml文件读取器,仅作了解
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
         beanDefinitionReader.setEnvironment(this.getEnvironment());        
         beanDefinitionReader.setResourceLoader(this);    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   initBeanDefinitionReader(beanDefinitionReader);     //这里才是核心,加载beanDefinition的工作还没开始 loadBeanDefinitions(beanDefinitionReader); }

    继续跟进去,这里依然“没干正事”:加载spring配置文件

    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);
            }
        }

    接着看核心代码

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader instanceof ResourcePatternResolver) {
                // 通配符模式匹配资源,转换为Resource对象。spring提供了多种ResourceLoader,根据通配符匹配,生成对应类型的Resource
                    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
              //【继续把加载工作往后放】
              int loadCount = loadBeanDefinitions(resources);return loadCount; } } else { // 以绝对路径加载单个资源文件,转换为Resource对象 Resource resource = resourceLoader.getResource(location);
           //【继续把加载工作往后放】 int loadCount = loadBeanDefinitions(resource);return loadCount; } }

    通过上面一步,把配置资源转化为Resource对象,然后作为参数传入loadxxx方法里进行解析。

    进入下面的实现发现,依然在做准备工作:将Resource读取为流

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
         //
            Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
            if (currentResources == null) {
                currentResources = new HashSet<EncodedResource>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
            if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            }
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                   //终于到do...是不是这里就开始真正的执行加载了?
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());       
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "IOException parsing XML document from " + encodedResource.getResource(), ex);
            }
            finally {
                currentResources.remove(encodedResource);
                if (currentResources.isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }
            }
        }

    来看下,删除非核心代码,就做了两件事,先读取资源对象Resource,封装成Document对象;再“注册”beanDefinition。

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
                //生成Document对象
                Document doc = doLoadDocument(inputSource, resource);
            //注册BeanDefinition
                return registerBeanDefinitions(doc, resource);
            
        }

    中间又经历了n个准备环境,最终进入方法parseBeanDefinitions,拿到了Document对象的根节点,开始调用解析方法解析节点:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            if (delegate.isDefaultNamespace(root)) {
                NodeList nl = root.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    Node node = nl.item(i);
                    if (node instanceof Element) {
                        Element ele = (Element) node;
                        if (delegate.isDefaultNamespace(ele)) {
                            parseDefaultElement(ele, delegate);
                        }
                        else {
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            }
            else {
                delegate.parseCustomElement(root);
            }
        }

    具体的解析逻辑,可以参考以下文章:https://blog.csdn.net/v123411739/article/details/86669952

    BeanDefinition包含的主要内容:

    @todo

    解析完成后,依然是注入到BeanFactory中缓存起来,供后续使用,主要的内容是两部分:

    1.beanDefinitionNames

    2.beanDefinitionMap

    总结:

    obtainFreshBeanFactory()方法的主要作用:
    1.创建beanFactory
    2.根据web.xml中contextConfigLocation配置的路径,读取Spring配置文件,封装为Resource
    3.根据Resource加载XML配置文件(bean文件)并解析为Document对象
    4.遍历Document,解析为beanDefinition。
  • 相关阅读:
    【转载】【贪心】各种覆盖问题
    【转载】【知识点总结】NOIP前夕 2014.11.4
    最大子图形问题
    小知识
    Tyvj——P1864 [Poetize I]守卫者的挑战
    Tyvj——P1952 Easy
    BZOJ——2134: 单选错位
    BZOJ——1620: [Usaco2008 Nov]Time Management 时间管理
    BZOJ——1622: [Usaco2008 Open]Word Power 名字的能量
    洛谷 U3357 C2-走楼梯
  • 原文地址:https://www.cnblogs.com/xyang/p/12357754.html
Copyright © 2020-2023  润新知