spring加载配置

2017-12-22  本文已影响47人  binecy

源码分析基于spring 4.3.x

本文通过阅读源码,分析spring加载配置文件的过程

小栗子

bean类

public class Blog {
    private String title;

    ... getter and settter
}

spring.xml配置

    <bean id="blog" class="spring.bean.Blog">
        <property name="title" value="hello spring"></property>
    </bean>

测试方法

    @Test
    public void test() {
        BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));
        Blog bean = (Blog)xmlBeanFactory.getBean("blog");

        Assert.assertEquals(bean.getTitle(), "hello spring");
    }

解析

XmlBeanFactory继承于DefaultListableBeanFactoryDefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载的bean的默认实现。 XmlBeanFactory只是使用了自定义的XML读取器XmlBeanDefinitionReader

但本篇文章就关注XmlBeanDefinitionReader

跟踪XmlBeanFactory的构造方法,


private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public /home/binecy/work/bin-learning(Resource resource, BeanFactory parentBeanFactory) ... {
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);  // 注:this.reader为XmlBeanDefinitionReader
}

接口结构

reader.png

XmlBeanFactory实现了BeanDefinitionRegistry, 可以将beanDefinition注册到spring环境
XmlBeanDefinitionReader实现了BeanDefinitionReader, 实现核心方法loadBeanDefinitions, 该方法复制读取xml文件,并加载到spring中, 是spring加载配置的核心。
BeanDefinitionReader中有一个关键属性BeanDefinitionRegistry, 这里就是XmlBeanFactory(XmlBeanDefinitionReader构造参数的this就是XmlBeanFactory)

这里会调用到XmlBeanDefinitionReader.loadBeanDefinitions方法:

public int loadBeanDefinitions(EncodedResource encodedResource)  {  
    //获取配置文件的InputStream
    InputStream inputStream = encodedResource.getResource().getInputStream();   
        try {
        // 构造InputSource
        InputSource inputSource = new InputSource(inputStream); 
        // 解析InputSource
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());   
      }
      finally {
            inputStream.close();
    }  
}

// 查看 解析过程 
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  {
    // 获取Document对象
    Document doc = doLoadDocument(inputSource, resource);   
    // 解析Document并注册到spring
    return registerBeanDefinitions(doc, resource);  
}  

// 查看Document解析过程
public int registerBeanDefinitions(Document doc, Resource resource) {
    // 创建DefaultBeanDefinitionDocumentReader对象
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 
    // 解析documentReader
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 
}

先看一下createReaderContext

public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
            this.sourceExtractor, this, getNamespaceHandlerResolver());
}

XmlReaderContext就是一个上下文, 这里把this(就是XmlBeanDefinitionReader), resource等信息放到了这个上下文中。

再看看BeanDefinitionDocumentReader, 他是一个简单的接口, 只有一个方法registerBeanDefinitions和一个实现类DefaultBeanDefinitionDocumentReader, 但对xml的解析基本都在这个类了:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 获取root元素
    Element root = doc.getDocumentElement();    
    // 解析root元素
    doRegisterBeanDefinitions(root);    
}
   
protected void doRegisterBeanDefinitions(Element root) {
    // 创建BeanDefinitionParserDelegate对象
    this.delegate = createDelegate(getReaderContext(), root, parent);   
    preProcessXml(root);    // 模板方法,提供给子类扩展
    parseBeanDefinitions(root, this.delegate);      // 解析root元素
    postProcessXml(root);   // 模板方法,提供给子类扩展
}
  
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); // 解析node结点
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

上述方法根据Namespace Uri判断node是否为Spring定义的元素,如果是,则调用parseDefaultElement方法解析元素。否则调用自定义的解析方法。
用户可以自定义标签及标签解析器, 文章最后会提到。

下面看看默认的解析方法:

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 解析import标签
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 解析alias标签
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  // 解析bean标签
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {  // 解析beans标签
            doRegisterBeanDefinitions(ele);
        }
    }

parseDefaultElement方法对import,alias,bean,beans标签进行了解析,这里主要看bean的解析过程:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 解析元素,解析结果为BeanDefinitionHolder对象
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);   
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);    // 装饰模式
        
        // 注册BeanDefinition,即将解析结果存储在registry对象中.
        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());    
        
        // 发送事件
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

这里出现了一个很重要的角色BeanDefinition。

BeanDefinition是spring对bean的抽象, 是配置文件中<bean>标签在spring容器中的内部表示类, 存储了bealn的相关信息, 如属性PropertyValues, BeanClassName,Scope等

beanDefinition.png

BeanDefinition有两个实现类ChildBeanDefinition和RootBeanDefinition。

BeanDefinition存在子类RootBeanDefinition和ChildBeanDefinition。
ChildBeanDefinition表示child <bean>, child <bean>parent中继承父类的构造函数参数值,属性值和方法覆盖,并且可以添加新的值。
RootBeanDefinition表示没有parent的bean

spring会将配置文件中<bean>配置信息转化为beanDefinition对象,并注册到容器中。

BeanDefinitionHolder中持有BeanDefinition实例, 同时附带了beanName和aliases属性。

回来看看delegate.parseBeanDefinitionElement,这里调用BeanDefinitionParserDelegate.parseBeanDefinitionElement方法:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    // 解析bean元素
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

    String beanClassName = beanDefinition.getBeanClassName();

    String[] aliasesArray = StringUtils.toStringArray(aliases);
    return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

// 查看bean元素的解析过程
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {        
    className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); // 解析className

    parent = ele.getAttribute(PARENT_ATTRIBUTE);


    AbstractBeanDefinition bd = createBeanDefinition(className, parent);

    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);   // 解析"scope","scope"等属性
    bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

    parseMetaElements(ele, bd); // 解析meta属性
    parseLookupOverrideSubElements(ele, bd.getMethodOverrides());   // 处理lookup-method元素
    parseReplacedMethodSubElements(ele, bd.getMethodOverrides());   // 处理replaced-method元素

    parseConstructorArgElements(ele, bd);   // 解析constructor-arg构造方法参数
    parsePropertyElements(ele, bd); // 解析property元素
    parseQualifierElements(ele, bd);    //解析qualifier元素

    bd.setResource(this.readerContext.getResource());
    bd.setSource(extractSource(ele));

    return bd;
}

看一下属性的解析过程parsePropertyElements

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节点
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    // 解析property节点Value
    Object val = parsePropertyValue(ele, bd, propertyName);
    PropertyValue pv = new PropertyValue(propertyName, val);
    parseMetaElements(ele, pv);
    pv.setSource(extractSource(ele));
    // 解析结果添加到BeanDefinition 
    bd.getPropertyValues().addPropertyValue(pv);        
}

// 解析property节点的Value
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
    boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

    if (hasRefAttribute) {  // 如果有ref属性
        String refName = ele.getAttribute(REF_ATTRIBUTE);
        
        RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    }
    else if (hasValueAttribute) {       // 如果有value属性
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    } else if (subElement != null) {
        // 如果有子标签,如list,set
        return parsePropertySubElement(subElement, bd);
    }
    ...
}

可以看到,解析property节点主要解析它ref属性(结果为RuntimeBeanReference)或value属性(结果为TypedStringValue)或子标签,并将结果添加到BeanDefinition 中,供spring生成bean时使用。

可以看看子标签的解析

public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
    if (!isDefaultNamespace(ele)) {
        return parseNestedCustomElement(ele, bd);
    }
    else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
        ...
    }
    else if (nodeNameEquals(ele, REF_ELEMENT)) {
        ...
    }
    else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
        return parseIdRefElement(ele);
    }
    else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
        return parseValueElement(ele, defaultValueType);
    }
    ...
}

可以看到,这里对各种类型的子标签(bean,ref,array,list等)进行了解析。这里不再对每一类型的子标签展开了。

最后看一下注册过程BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) ...{

    // 为bean注册beanName
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 为bean注册别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}
        

getReaderContext().getRegistry()获取到XmlBeanFactory, registry.registerBeanDefinition最终将调用到DefaultListableBeanFactory.registerBeanDefinition方法:

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ... {
        this.beanDefinitionMap.put(beanName, beanDefinition);

    }   

最后,bean解析结果beanDefinition存储在DefaultListableBeanFactory的beanDefinitionMap属性中(实际上就是map对象)。

最后, 再看一下自定义标签的处理, 将调用BeanDefinitionParserDelegate.parseCustomElement

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    // 获取对应的NamespaceHandler
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    // 调用NamespaceHandler处理
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

这里可以看到, 自定义标签主要使用自定义的NamespaceHandler进行处理。

上一篇下一篇

猜你喜欢

热点阅读