对于经常使用spring框架的同学,对于下面的这段代码肯定不会陌生

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

(1)读取配置文件applicationContext
(2)找到配置文件中定义的配置并实例化

以上是Spring实现容器的基础,虽然只有短短的一行,但是里面却包含了复杂的逻辑。为了分析里面包含的逻辑,我们可以使用他的父类BeanFactory来分析。下面代码是Spring比较原始的实现容器的方法。

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

代码实现的逻辑为:

1.使用ClassPathResource将beanFactory.xml封装成Resource
2.完成XmlBeanFactory的初始化

ClassPathResource

在 Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而 URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring对其内部资源,使用了自己的抽象结构:Resource接口来封装。而 ClassPathResource实现类即是对Resource的实现。而Resource实现了InputStreamSource。

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

public interface Resource extends InputStreamSource {


    boolean exists();


    boolean isReadable();


    boolean isOpen();


    URL getURL() throws IOException;


    URI getURI() throws IOException;


    File getFile() throws IOException;


    long contentLength() throws IOException;


    long lastModified() throws IOException;


    Resource createRelative(String relativePath) throws IOException;


    String getFilename();


    String getDescription();
}

不同的Resource实现代表了不同来源的资源,对于来自ClassPath的资源就是用ClassPathResource来实现。

XmlBeanDefinitionReader

将配置文件封装好以后就需要对其读取,这个工作由XmlBeanDefinitionReader来完成。XmlBeanDefinitionReader中由loadBeanDefinitions方法实现读取的功能。loadBeanDefinitions方法的源代码如下:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //loadBeanDefinitions的具体实现,而EncodedResource主要用于对资源文件的处理
        return loadBeanDefinitions(new EncodedResource(resource));    
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
                logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
//通过属性来记录已经加载的资源

        。。。。。。。。。。

        // 调用DefaultResourceLoader的getResources方法完成具体的Resource定位 
        try {
                //从EncodedResource中获取已经封装的Resource对象并再次从Resource中获取inputStream 
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                        InputSource inputSource = new InputSource(inputStream);
                        if (encodedResource.getEncoding() != null) {
                                inputSource.setEncoding(encodedResource.getEncoding());
                        }
                         //真正的实现核心
                        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());   
                }
                。。。。。。。。。。。。。
}

对于上述源码,只需要知道实现逻辑:
(1)对于resource做一次封装,为了考虑到resource可能存在的编码问题,我们使用了new EncodedResource(resource)来实现。
(2)准备获得InputSource对象。
(3)将封装好的resource和InputSource传递给真正的核心部分:doLoadBeanDefinitions(inputSource, encodedResource.getResource());

doLoadBeanDefinitions

我们看一看doLoadBeanDefinitions(inputSource, encodedResource.getResource())中的实现方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

    //这里略去了冗余的代码,值需要注意三个方法
    //获得验证模式
    int validationMode = getValidationModeForResource(Resource resource)
    //获得document
    Document document = loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
    //注册Bean信息
    registerBeanDefinitions(Document doc, XmlReaderContext readerContext)            
}

doLoadBeanDefinitions只做了三件事:
1.获得验证模式
2.获得document
3.注册Bean信息

获得验证模式

验证模式就两种,DTD和XSD。如果xml文件中有则表示使用DTD验证模式,否则使用XSD。源代码如下,比较简单。

protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        //如果手动指定了验证模式则使用指定的验证模式
        if (validationModeToUse != VALIDATION_AUTO) {
                return validationModeToUse;
        }
        //如果没有指定,则自动检测
        int detectedMode = detectValidationMode(resource);    //自动检测主要是在detectValidationMode(Resource resource)完成的
        if (detectedMode != VALIDATION_AUTO) {
                return detectedMode;
        }
        return VALIDATION_XSD;
}

获得Document

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
                logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
}

逻辑:
1.创建DocumentBuilderFactory
2.通过DocumentBuilderFactory创建DocumentBuilder
3.DocumentBuilder解析inputSource返回一个Document对象。

registerBeanDefinitions

获得了XML文档文件的Document以后,就会执行BeanDefinitions的解析和注册。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析 
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();

        // 具体的解析过程在BeanDefinitionDocumentReader的registerBeanDefinitions方法中完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;    
}

registerBeanDefinitions对前一步获得的Document进行了处理,实现注册和解析由方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))实现。

documentReader中的registerBeanDefinitions方法如下:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();    // 获得Document的根元素
        //核心逻辑
        doRegisterBeanDefinitions(root);
}

真正的实现XML文件解析和加载的核心部分——doRegisterBeanDefinitions方法

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                                return;
                        }
                }
        }

        preProcessXml(root);    // 解析Bean定义之前, 增强解析过程的可扩展性 
        parseBeanDefinitions(root, this.delegate);    // 从Document的根元素开始进行Bean定义的Document对象  
        postProcessXml(root);    // 解析Bean定义之前, 增强解析过程的可扩展性  

        this.delegate = parent;
}

核心代码逻辑如下:
1.对profile属性的处理。不同的profile属性可以理解为:代表了xml中beans的不同namespace,一个namespace代表一套的配置方案。

2.preProcessXml方法。在解析之前,我们可以根据需要实现一些逻辑,需要我们自己实现。

3.parseBeanDefinitions方法,对xml配置文件的读取和解析。

4.postProcessXml,在解析完成之后,我们可以根据需要实现一些逻辑,需要我们自己实现。

总结

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

1.beanFactory.xml→ClassPathResource
2.使用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)读取Resource。

loadBeanDefinitions(resource)的实现逻辑:
2.1.封装资源,new EncodeResource(resource);
2.2.获得输入流,并构造inputsource。
2.3.调用doLoadBeanDefinitions(inputsource,resource)。

doLoadBeanDefinitions(inputsource,resource)的实现:
2.3.1.获得XML验证模式:
方法:getValidationModeForResource
2.3.2.加载XML文件,获得Document类。
方法:loadDocument
2.3.3.根据Document获得Bean。
方法:registerBeanDefinitions(document,resource);

registerBeanDefinitions(document,resource)中的doRegisterBeanDefinitions方法是真正的核心实现。

doRegisterBeanDefinitions实现逻辑:
1.处理profile
2.preProcessXml
3.parseBeanDefinitions方法,对xml配置文件的读取和解析
4.postProcessXml