Spring源码系列——BeanDefinition载入(中)

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Spring源码系列——BeanDefinition载入(中)

  • Web核心-Servlet
  • Spring源码系列:启动过程
  • Spring源码系列:容器刷新
  • Spring源码系列:BeanFactory的创建
  • Spring源码系列:BeanDefinition源码解析
  • Spring源码系列:BeanDefinition载入(上)
  • Spring源码系列:BeanDefinition载入(中)
  • Spring源码系列:BeanDefinition载入(下)
  • 上一篇是将Bean的解析注册流程进行了梳理,对于一些细节问题没有进行细究,比如说元素属性值的处理,构造函数的处理等等。本篇就学习记录一下相关点。

    首先来看下是在哪个地方具体生成BeanDefinitiond的。下面是方法请求的顺序。

  • 1. DefaultBeanDefinitionDocumentReader.parseDefaultElement
  • 2. DefaultBeanDefinitionDocumentReader.processBeanDefinition
  • 3. BeanDefinitionParserDelegate.parseBeanDefinitionElement
  • 关于元素的解析绝大多数都是在BeanDefinitionParserDelegate及其子类中完成的。OK,来看下parseBeanDefinitionElement这个方法:

    
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {
    
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        //在这里是读取bean的class名字,然后载入到BeanDefinition中,并未做实例化
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
    
        try {
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            //生成BeanDefinition对象
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //解析当前bean的属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //设置description信息
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            //对bean的元素信息进行解析
            parseMetaElements(ele, bd);
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析bean的构造函数设置
            parseConstructorArgElements(ele, bd);
            //解析property设置
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);
    
            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));
    
            return bd;
        }
        //异常1:ClassNotFoundException
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        //异常2:NoClassDefFoundError
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        //其他未知错误
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }
    
        return null;
    }
    

    此处我们以解析property为例,看下具体的处理细节:

    
    //解析给定bean元素的属性子元素。
    public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        //获取子元素节点
        NodeList nl = beanEle.getChildNodes();
        //遍历
        for (int i = 0; i  nl.getLength(); i++) {
            Node node = nl.item(i);
            //是否包含property标识
            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                parsePropertyElement((Element) node, bd);
            }
        }
    }
    

    接着是执行具体property,在parsePropertyElement中完成:

    
    //解析一个property元素。
    public void parsePropertyElement(Element ele, BeanDefinition bd) {  
        //首先获取到property的名称
        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
        //检查是否有name
        if (!StringUtils.hasLength(propertyName)) {
            error("Tag 'property' must have a 'name' attribute", ele);
            return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
           //验证在同一个bean中存在同名的property,存在的话就不解析了,直接返回
            if (bd.getPropertyValues().contains(propertyName)) {
                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                return;
            }
            //解析出property的值
            Object val = parsePropertyValue(ele, bd, propertyName);
            //封装成PropertyValue对象
            PropertyValue pv = new PropertyValue(propertyName, val);
            parseMetaElements(ele, pv);
            pv.setSource(extractSource(ele));
            bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
            this.parseState.pop();
        }
    }
    

    在parsePropertyValue中,是对所有的property子元素进行具体解析的。我们知道property中除了单值之外,还会包括如:list,set,map,prop等集合元素;这些都会被封装成对应的Managerd对象。比如:ManagedList等。不同的集合类型页同样对应一种解析方法,比如解析list的是使用parseListElement。这些解析都是在BeanDefinitionParserDelegate类中完成的。这个后面我会抽一篇来学习BeanDefinitionParserDelegate这个类。

    Bean的载入过程就是这样通过层层解析来完成的,但是对于目前的Ioc容器来说,仅仅是完成了对Bean对象管理的一些数据准备工作,也就是初始化工作,目前的BeanDefginiton中包含的就是一些静态的配置信息,Bean的实例化还没有进行,这个实例化的过程是在依赖注入时候完成的。

    原文始发于微信公众号(glmapper工作室):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Spring源码系列——BeanDefinition载入(中)


     上一篇
    Spring源码系列——Spring的版本变更 Spring源码系列——Spring的版本变更
    Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,S
    2021-04-05
    下一篇 
    Spring源码系列——依赖注入(二)createBean Spring源码系列——依赖注入(二)createBean
    在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的。createBean的具体实现是在Abst
    2021-04-05