上一篇博客说明了下spring是如何找到资源文件的,classpath下的xml,最终会被解析为 ClassPathContextResource
,下面进一步分析,有了这个资源文件之后spring是如何将其解析为BeanDefinition的
入口XmlBeanDefinitionReader.loadBeanDefinitions
最开始的入口,只是包了下Resource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//EncodedResource只是存了Resource的编码
return loadBeanDefinitions(new EncodedResource(resource));
}
1)保存当前正在加载的资源
2)检测是否有重复加载资源的情况
3)真正干活的地方doLoadBeanDefinitions
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//... 省略日志
//1、保存当前正在加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//2、检测是否有重复加载资源的情况
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//document加载需要的的InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//3、真正干活的地方doLoadBeanDefinitions
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
//... 省略 一系列异常处理
}
1、这里做的两件事,将资源文件解析成Document
2、解析并注册BeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
document的解析
XmlBeanDefinitionReader 将 Document的生成,委托给了DocumentLoader
看下默认实现 DefaultDocumentLoader
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//模板方法模式,子类继承DefaultDocumentLoader后可以替换DocumentBuilderFactory和DocumentBuilder的实现
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);
}
这里使用了工厂方法模式
解析xml 并 注册BeanDefinition
有了document之后,就可以很方便的通过document的API来读取xml的元素。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//beanDefininition的注册,委托给了BeanDefnitionDocumentReader
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
具体的注册调用过程,就不跟了,总之,最后会调用到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法
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);
}
}
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);
}
}
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);
}
}
到了这一步,spring开始配置文件解析,具体如何解析,以及如何将配置文件封装为BeanDefinitions并进行注册,下一篇博客会分析。