Spring源码分析-容器基础

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012006689/article/details/79942527

spring版本是3.2.18,为什么选择这么个相对旧的版本?spring代码过于复杂,参考着郝佳写的《Spring源码深入解析》来学习,会变得容易一些,这本书使用的就是3.2版本。要分析的spring功能主要是IOC、AOP、事务、Spring MVC,即便是spring新版本,对这几个功能的实现变化不大。

从一个基本的语句入手

  • spring-test.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="testCat" class="Cat"/>
</beans>
  • 测试代码
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-test.xml"));
Cat cat = (Cat) bf.getBean("testCat");

Resource-配置文件的封装

Spring使用Resource接口封装资源。其家族类图如下:
这里写图片描述
对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)。前两个挺常用的。

自动装配时忽略给定的依赖接口

在执行new XmlBeanFactory()时,会执行父类AbstractAutowireCapableBeanFactory的如下代码:

public AbstractAutowireCapableBeanFactory() {
    super();
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
}

这三行ignore是忽略什么呢?假设类A中有属性类B,那么在获取A的bean的时候,如果B还没有初始化,Spring此时会自动初始化B。这里的ignore,就是当B实现了BeanNameAware、BeanFactoryAware、BeanClassLoaderAware这三个接口的某一个时,就不会自动初始化B了。

获取XML的验证模式:DTD还是XSD?

如果xml使用DTD,会在spring-test.xml的头部位置声明:

<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/Spring-beans-2.0.dtd">

而如果XML使用XSD,就是上文中示例的配置。Spring判断验证模式的方式就是看xml头部是否有“DOCTYPE”字符串,有就是DTD,否则就是XSD。

使用JAXP DOM方式解析XML

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

这里书的作者说是SAX解析,这是错误的。SAX解析会为XML每一部分的解析绑定处理器,不会最终得到一个Document。这里就是使用JAXP的DOM方式来解析XML的。JAXP的使用可以参考:JAXP进行DOM和SAX解析

EntityResolver:由程序定义XSD文件的来源

这里不讨论已经被淘汰的DTD了,只讨论XSD。解析一个XML,会先根据XSD的地址去网络上下载对应的XSD声明,然后对XML进行验证。如果网络出现中断或不可用问题,就会报出XSD声明没有被找到的信息。EntityResolver的作用是由程序来实现寻找XSD声明的过程。
EntityResolver接口方法声明:

public abstract InputSource resolveEntity (String publicId, String systemId)throws SAXException, IOException;

对于XSD验证模式而言,

publicId:null
systemId:http://www.springframework.org/schema/beans/spring-beans.xsd 

加载XSD类型的PluggableSchemaResolver类的resolveEntity是默认到META-INF/Spring.schemas文件中找到systemid所对应的XSD文件并加载。
这里写图片描述
在spring.schemas文件中,根据systemId找到了xsd的具体存储位置:

http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsd

标签解析,分别对待

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

获取到XML Document之后,就可以进行解析Bean了。但XML配置文件中存在两类Bean声明,一类是默认的:

<bean id="cat" Class="com.tony.Cat"/>

另一类是自定义的标签,比如:

<tx:annotation-driven/>

这两类标签的解析方式差别很大。对于自定义的标签,需要用户实现一些接口与配置。判断节点是否默认命名空间就是将node.getNamespaceURI()获取到的命名空间和Spring固定的命名空间http://www.Springframework.org/schema/beans 进行对比。一致就是默认标签,否则就是自定义标签。

两类标签的具体解析方式下一篇博客介绍。

猜你喜欢

转载自blog.csdn.net/u012006689/article/details/79942527