Spring Framework框架解析(1)- 从图书馆示例来看xml文件的加载过程

引言

这个系列是我阅读Spring源码后的一个总结,会从Spring Framework框架的总体结构进行分析,不会先入为主的讲解IOC或者AOP的原理,若是读者有使用Spring的经验再好不过。鉴于每一个人对源码阅读角度的不一样,若是文中存在理解有误的地方但愿读者可以及时提出,共同进步。文章所分析的源码基于5.0.8版本,但使用老版本理解起来问题也不大,由于在框架总体架构上变化并很少。java

若是你使用Spring的时间足够长,相信在最初的开发过程当中你必定使用过xml文件来加载各中bean。虽然如今基本都会经过配置文件类或者注解来进行加载,但使用xml也有它的优势,这种方式对代码的侵入性最小,并且配置第三方bean也比较方便。这篇文章经过一个图书馆的例子来说解xml最原始的加载过程,将加载过程当中涉及到的各个模块比作图书馆的各个元素,但愿能加深你对Spring框架的理解。node

图书馆和Spring有许多类似的地方,将图书馆比作bean工厂,从图书馆借书至关于getBean的过程,将图书馆买的书放入图书馆的过程能够类比注册bean(registerBeanDefinition)的过程,而生产图书的过程又能够类比实例化BeanDefinition的过程,是否是很类似?这里我会使用下面一段比较原始的代码来分步讲解这一过程。架构

ClassPathResource resource = new ClassPathResource("applicationContext.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 
reader.loadBeanDefinitions(resource);
Object beanObject = factory.getBean("beanName");

ClassPathResource与Resource

ClassPathResource resource = new ClassPathResource("applicationContext.xml");

这一行代码比较简单,它经过一个xml文件初始化了一个Resource,至关于对xml文件作了一个包装,方便之后将xml文件转换为BeanDefinition。能够将这一过程想象成如今的图书仍是一堆木头,而将这些木头搅拌成木浆只是为了后面更方便的获取制做图书的原料而已。app

从源码角度来讲ClassPathResource继承自Resource接口,是Spring中对资源的抽象,全部须要使用的资源在Spring中都被抽象为Resource,它提供了一系列操做资源的方法,好比获取资源的名称,资源是否存在等等。Resource接口又继承了InputStreamSource接口,在InputStreamSource中,提供了一个核心方法,这个方法将资源转换成InputStream方便后期操做。框架

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}

public interface Resource extends InputStreamSource {
    boolean exists();

    URL getURL() throws IOException;

    String getFilename();

    ......
}

BeanFactory、BeanDefinition与DefaultListableBeanFactory

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

DefaultListableBeanFactory就比较重要了,它是一个Bean工厂,至关于图书馆,全部的书都在DefaultListableBeanFactory中,而借书,买书的过程都须要经过DefaultListableBeanFactory来操做。post

DefaultListableBeanFactory首先是BeanFactory接口的一个实现,BeanFactory定义了经过名称和类型获取Bean的一系列方法。ui

public interface BeanFactory {
    Object getBean(String name) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    boolean containsBean(String name);

    ......
}

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable{}

其次从DefaultListableBeanFactory的定义还能够看到,它在继承BeanFactory接口的基础上,还实现了BeanDefinitionRegistry接口。BeanDefinitionRegistry的核心功能是对Bean的注册,注册是干吗呢?经过图书馆来对比,BeanFactory的getBean至关于从图书馆借书,那么这些书是哪来的呢?就是经过BeanDefinitionRegistry的registerBeanDefinition方法,它至关于把书放入图书馆,而DefaultListableBeanFactory就至关于图书馆自己了。this

public interface BeanDefinitionRegistry extends AliasRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    ......
}

在BeanDefinitionRegistry的定义中还有涉及到一个关键接口:BeanDefinition,上面说BeanDefinitionRegistry至关于把书放入图书馆,那么具体图书在图书馆中怎么表示呢?这就是BeanDefinition。BeanDefinition是Bean在Spring中的抽象,也就是说每个Bean在Spring中都对应一个BeanDefinition,它提供了与Bean相对应的属性,并提供了操做Bean的方法。spa

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    void setBeanClassName(@Nullable String beanClassName);

    String getBeanClassName();

    void setScope(@Nullable String scope);

    String getScope();

    ......
}

BeanDefinitionReader、BeanDefinitionDocumentReader与XmlBeanDefinitionReader

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 
reader.loadBeanDefinitions(resource);

仍是经过图书馆来类比:图书馆是(DefaultListableBeanFactory),把书(BeanDefinition)放入图书馆的能力对应(BeanDefinitionRegistry),从图书馆拿编号后的书的能力对应(BeanFactory),书的原材料对应(ClassPathResource),如今就缺把书的原材料(ClassPathResource)变成一本本书(BeanDefinition),并将它放入图书馆中了。那么谁来将原材料(ClassPathResource)变成书(BeanDefinition)并放入到图书馆(DefaultListableBeanFactory)中呢?这就是XmlBeanDefinitionReader的工做了。这一过程能够经过如下源码来分析:debug

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader{}
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader{}

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
    super(registry);
}

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
    ......
    this.registry = registry;
    ......
}

public interface BeanDefinitionReader {
    BeanDefinitionRegistry getRegistry();

    ResourceLoader getResourceLoader();

    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
    ......
}

首先XmlBeanDefinitionReader实现了BeanDefinitionReader接口,BeanDefinitionReader定义了一个关键方法loadBeanDefinitions(Resource resource),这个方法将resource装载到BeanDefinitionRegistry中,BeanDefinitionRegistry经过XmlBeanDefinitionReader的构造方法传入。具体loadBeanDefinitions又是怎么作的呢?再来继续查看源代码:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    ......
        //经过InputStreamSource接口的定义的getInputStream方法获取InputStream
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            //将InputStream包装成InputSource
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //将Source装载到BeanDefinitionRegistry中
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            inputStream.close();
        }
    ......
}

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    ......
    try {
        //将Source包装成Document
        Document doc = doLoadDocument(inputSource, resource);
        //将Document装载到BeanDefinitionRegistry中
        return registerBeanDefinitions(doc, resource);
    }
    ......
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //这里建立了一个DefaultBeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();

    //调用DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法将将Document装载到BeanDefinitionRegistry中
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

首先将Resource转换为EncodedResource,而后经过getInputStream获取InputStream,调用doLoadBeanDefinitions方法来装载资源,在doLoadBeanDefinitions方法中,首先将Resource包装成Document方便操做元素节点,而后把解析并装载Document的功能委托了给BeanDefinitionDocumentReader,这里使用了一个默认的DefaultBeanDefinitionDocumentReader实现。那么能够想象DefaultBeanDefinitionDocumentReader中作了两件事:将Document解析为BeanDefinitions,而后将BeanDefinitions装载到BeanDefinitionRegistry中。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    //从Document中获取到Element
    Element root = doc.getDocumentElement();

    //具体的解析过程
    doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
    ......
    //前置解析,默认为空,能够重写
    preProcessXml(root);
    //具体的解析xml并注入到过程
    parseBeanDefinitions(root, this.delegate);
    //后置解析,默认为空,能够重写
    postProcessXml(root);
    ......
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    ......
    parseDefaultElement(ele, delegate);
    ......
}

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

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    //将元素包装成BeanDefinitionHolder,方便操做BeanDefinition
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            //具体的注入方法
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        ......
    }
}

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {
    ......
    //经过BeanDefinitionRegistry将元素注入到DefaultListableBeanFactory中
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    ......
}

跟以前猜想的同样,首先经过parseBeanDefinitions方法将全部的xml节点分步解析,以后将解析后的节点包装成BeanDefinitionHolder对象,最后经过BeanDefinitionRegistry的registerBeanDefinition方法将元素注入到BeanDefinitionRegistry中。

整个解析到注入过程很是复杂,我只列出了核心步骤,从中能够看到XmlBeanDefinitionReader是怎么一步步将xml中的Bean节点变为BeanDefinition并放入到DefaultListableBeanFactory中的。仍是用图书馆来类比:首先将原材料(ClassPathResource)变成纸张(Document),而后将纸张(Document)经过书籍制造工厂(BeanDefinitionDocumentReader)组装成一本本书籍(BeanDefinition),而后书籍制造工厂(BeanDefinitionDocumentReader)将一本本书籍(BeanDefinition)送到图书馆(DefaultListableBeanFactory),而XmlBeanDefinitionReader就扮演了这一整个过程的组合功能。

总结

至此,整个图书馆功能就齐全了,原材料能够造书,书能够放入图书馆,而且你也能够很方便的从图书馆借书。能够说Spring设计理念也在这一过程当中获得体现,它将Bean的解析,Bean的定义,Bean的生产以及Bean的获取每一步都单独抽离开来,互不干扰,最后经过DefaultListableBeanFactory将它们整合到一块儿供用户使用。怎么说呢,这一过程回过头来并无什么神奇的地方,但能清晰的将每一个功能都抽象出来自己就须要很是好的抽象设计能力,而对这一过程的反复阅读与分析,必定能让你在设计抽象能力上有必定的提高。

看完后你以为这一过程类比是否恰当呢?若是你有更贴近生活的例子,不妨留言一块儿探讨,共同进步。说完DefaultListableBeanFactory,在下一篇文章中将会讲讲ApplicationContext接口,并对它的部分实现类作一个简单分析。

参考资料:

  • 《Spring揭秘》
  • 《Spring源码深度解析》
相关文章
相关标签/搜索