开始对spring framwork explore,以下以及后续相关的文章都是自己一边看赫佳同志的《Spring 源码深度解析》一边做的笔记,比较潦草,有不对的地方希望多多指教。
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
这行代码完成了applicationContext.xml的解析和注册
大致流程:
- 使用Resource接口实现类和InputResource来封装资源文件
- 使用DefaultDocumentLoader将inputSource资源转换成Document
- 使用DefaultBeanDefinitionDocumentReader注册beanDefinition.
ClassPathResource Resource接口
封装配置文件
XmlBeanFactory
构造函数调用父类(AbstractAutowireCapableBeanFactory)构造函数
AbstractAutowireCapableBeanFactory
ignoreDependencyInterface自动装配时忽略给定的依赖接口,BeanNameAware.class,BeanFactoryAware.class
例如ApplicationContext通过ApplicationContextAware进行注入
为啥要忽略这些依赖接口?
例如:实现了ApplicationContextAware接口的类,有一个setApplicationContext方法,这个方法是spring容器执行的,这样bean可以直接访问spring容器
this.reader.loadBeanDefinitions(resource);
XmlBeanDefinitionReader
1. EncodedResource 封装资源
2. ThreadLocal<Set<EncodedResource>>
设置当前的加载资源
doLoadBeanDefinitions(inputSource, encodedResource.getResource());
核心在这里:
1. loadDocument Document doc = doLoadDocument(inputSource, resource);
1.1. getEntityResolver.在项目中,寻找DTD声明,验证文档有效性。
使用EntityResolver的目的是不去网上下载对应的XSD/DTD文档,而是本地默认存在的,提高性能。
public InputSource resolveEntity(String publicId, String systemId)
通过publicId和systemId来寻找,systemId对应的就是beans.xml中声明部分xsi:schemaLocation中的第二部分(XML Schema文件位置)。在META-INF/spring.schemas文件中存在网络XML Schema文件位置和本地XML Schema文件位置的映射。
1.2. getValidationModeForResource 获取验证模式DTD OR XSD
DefaultDocumentLoader
借助java原生组件解析并得到document
1. 通过DocumentBuilderFactory获取DocumentBuilder
2. 解析InputSource(A single input source for an XML entity.)
- registerBeanDefinitions
return registerBeanDefinitions(doc, resource);
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
DefaultBeanDefinitionDocumentReader
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);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
parseBeanDefinitions(root, this.delegate);
中判断是默认的bean定义还是自定义的。
<bean id="test" class="test.TestBean" />
<tx:annotation-driven />
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
默认标签的解析
processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
BeanDefinitionParserDelegate
parseBeanDefinitionElement
解析下面的所有字节
parseBeanDefinitionAttributes
解析字节的属性
BeanDefinition 是配置文件元素标签在在容器中的内部表示形式。
然后spring把BeanDefinition注册到BeanDefinitionRegistry中。
BeanDefinitionRegistry就是Spring配置信息的内存数据库,以map形式保存。
lookup-method 作用是把一个方法声明为返回某种类型的bean
以parseConstructorArgElements(ele, bd);
代码为例
遍历获取<constructor-arg ../>
节点的index type name属性,解析当前constructor-arg节点下的构造函数的参数property,
有index的情况,property封装在ConstructorArgumentValues.ValueHolder valueHolder中,设置type name。把valueHolder与index一一映射保存起来
无index的情况.和上面的类似,区别是保存valueHolder时没有index.
那如何解析constructor-arg节点下属性呢?
- ref类型。找到ref节点,封装于RuntimeBeanReference中,设置source属性。
- value类型,封装于TypedStringValue,设置source属性。
一般节点被封装于一个valueHolder中,节点作为XmlReaderContext的extractSource(Element ele)的参数,保存在valueHolder的source中。
- 使用Resource接口实现类和InputResource来封装资源文件
- 使用DefaultDocumentLoader将inputSource资源转换成Document
- 使用DefaultBeanDefinitionDocumentReader注册beanDefinition.
对整体的解析流程再进行扩展一下
1.1 ClassPathResource 实现AbstractResource 典型的策略模式
2.1 getEntityResolver 作用是在当前项目下找到DTD的声明(不使用网络),然后验证文档的正确性
2.2 获得文档的验证模式,DTD OR XSD
2.3 spring 没有亲力亲为,使用javax.xml
3.1 利用BeanDefinitionParserDelegate解析bean(属性,constructor-arg,lookup等节点) import alias 等节点
3.2 利用BeanDefinitionParserDelegate解析默认标签中的自定义标签
3.3 使用BeanDefinitionReaderUtils注册BeanDefinition,byName,byAlias两种方式,其实就是把beandefinition放在map里面
3.4 通知监听器解析和注册过程完成 fireComponentRegistered