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 进行对比。一致就是默认标签,否则就是自定义标签。
两类标签的具体解析方式下一篇博客介绍。