您的位置:首页 > 编程语言 > Java开发

Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

2017-04-13 21:50 417 查看

前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory


前文提到最关键的地方是解析bean xml配置文件,废话不多说,直接上代码清单

//正如官方注释所说,解析import标签、alias标签、bean标签和自定义的标签
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//检查<beans>根标签的命名空间是否为空或者是http://www.springframework.org/schema/beans
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))
//解析import标签、alias标签、bean标签、内置<beans>标签
parseDefaultElement(ele, delegate);
}
else {
//解析自定义标签,例如<context:component-scan basePackage="" />
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

那我们根据两个部分来解读对bean xml文件的解析,分别为
DefaultBeanDefinitionDocumentReader#parseDefaultElement
BeanDefinitionParserDelegate#parseCustomElement


DefaultBeanDefinitionDocumentReader#parseDefaultElement

解析import标签、alias标签、bean标签,代码清单如下

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);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse 递归
doRegisterBeanDefinitions(ele);
}
}

三种解析具体方法

DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource

import
标签解析代码比较长,逻辑倒是很简单,代码清单如下

```java

protected void importBeanDefinitionResource(Element ele) {

//获取标签的resource属性值

String location = ele.getAttribute(RESOURCE_ATTRIBUTE);

//resource不允许为空

if (!StringUtils.hasText(location)) {

getReaderContext().error("Resource location must not be empty", ele);

return;

}

// Resolve system properties: e.g. "${user.dir}"
//这里强调下,对于<import>标签,其会从System.getProperties()和System.getenv()中属性获取,优先System.getProperties()
//即优先系统变量,环境变量次之
location = environment.resolveRequiredPlaceholders(location);

Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

// Discover whether the location is an absolute or relative URI
//resource属性支持URL模式,找到相应的资源并进行加载
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
}

// Absolute or relative?
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
// No URL -> considering resource location as relative to the current file.
try {
int importCount;
//相对路径,相对于当前文件
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
ele, ex);
}
}
//可以忽略以下操作
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));

}

```

DefaultBeanDefinitionDocumentReader#processAliasRegistration

主要目的就是缓存全局别名

java   protected void processAliasRegistration(Element ele) {       //这里确保<alias>标签的name、alias值不为空 e.g. <alias name="key" alias="value">       String name = ele.getAttribute(NAME_ATTRIBUTE);       String alias = ele.getAttribute(ALIAS_ATTRIBUTE);       boolean valid = true;       if (!StringUtils.hasText(name)) {           getReaderContext().error("Name must not be empty", ele);           valid = false;       }       if (!StringUtils.hasText(alias)) {           getReaderContext().error("Alias must not be empty", ele);           valid = false;       }       if (valid) {           try {               //全局设置,查阅后其会保存到DefaultListableFactory的父级类`SimpleAliasRegistry#aliasMap`中               getReaderContext().getRegistry().registerAlias(name, alias);           }           catch (Exception ex) {               getReaderContext().error("Failed to register alias '" + alias +                       "' for bean with name '" + name + "'", ele, ex);           }           getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));       }   }


DefaultBeanDefinitionDocumentReader#processBeanDefinition

<bean>
标签的解析处理

java   protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {       //初始化bean标签的相应内容       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);       if (bdHolder != null) {           //对bean标签中的属性和子标签,进行相应的具体解析,例如property、constructor-args           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

解析
bean
标签,也许会返回null在出现解析错误的时候,代码清单如下

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获取<bean>的 id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//如果有name属性,则获取name,并且可有多个name,以,;为分隔符
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}

String beanName = id;
//当id属性不存在且name属性有值时,默认使用第一个name值
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//确保beanName的唯一性,即在应用使用前不允许有两个的beanName一致
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//解析除id、name属性的情况
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//省略部分代码
...
...
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

return null;
}

限于篇幅太长,其余的情况我们就不讲解了,有兴趣的可以自行前往,但在此根据读到的源码对
bean
解析进行总结



parent
属性代表父类,这里表示的是父类的唯一id名

不允许有
singleton
属性;可有
scope
属性;

可有
abstract
属性,值可为true/false;

可有
lazyinit
属性,值为default则沿用
beans
标签中的
lazyinit
,否则为true/false;

可有
autowire
属性,值为default则沿用
beans
标签的
autowire
byName
表示按照名称注入、
byType
按照类型注入、
constructor
表明按照构造函数注入、
autodetect
表明自动检测(官方不建议使用)、默认则不使用注入;

可有
dependency-check
属性,值有
all
objects
simple
,默认为不检测;

可有
depends-on
属性,可有多值,以,;为分隔符;

可有
autowire-candidate
属性,值可为
true/false/default/空字符串


可有
primary
属性,值为true/false;

可有
init-method
属性,初始化方法,对应可有
destroy-method
属性;

可有
facroty-method
属性,工厂方法;

可有
factory-bean
属性

bean
标签下可有
description
标签,表示对此
bean
的描述;

也可有
meta
标签;

可有
lookup-method
标签;

可有
replaced-method
标签;

可有
constructor-arg
标签;

可有
property
标签,其中
value
ref
属性只可选用一个;

可有
qualifier
标签,其下可有
attribute
标签,同
@Qualifier
注解

所有的bean信息都由
BeanDefinitionHolder
对象来保存,其中的
BeanDefinition
包含了一个
bean
标签的所有可能内容



BeanDefinitionParserDelegate#parseCustomElement

解析自定义的标签节点,例如
<context:component-scan>
<tx:annotation-driven>
等,简单看下代码清单

//containingBd此处为null
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取命名空间
String namespaceUri = getNamespaceURI(ele);
//从map集合中获取NamespaceHandler接口
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//调用统一的解析接口
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

按照上述的注释,我们需要查看下
namespaceURI
对应的
NamespaceHandler
到底是如何获取的,真实调用的是
DefaultNamespaceHandlerResolver
类,稍微分析下此类

构造函数

```java

public DefaultNamespaceHandlerResolver() {

//第二个参数值为 META-INF/spring.handlers

this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);

}

//此处所用的是handlerMappingLocation,指的是handler接口类加载的文件资源

public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {

Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");

this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());

this.handlerMappingsLocation = handlerMappingsLocation;

}

```

resolve解析方法

java       public NamespaceHandler resolve(String namespaceUri) {       //调用的是PropertiesLoaderUtils.loadAllProperties读取所有classpath下的'META-INF/spring.handlers'文件,保存至Map集合       Map<String, Object> handlerMappings = getHandlerMappings();       Object handlerOrClassName = handlerMappings.get(namespaceUri);       if (handlerOrClassName == null) {           return null;       }       else if (handlerOrClassName instanceof NamespaceHandler) {           return (NamespaceHandler) handlerOrClassName;       }       else {           String className = (String) handlerOrClassName;           try {               Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);               //声明的handler必须是NamespaceHandler的实现类               if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {                   throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +                           "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");               }               //实例化Handler类               NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);               //初始化               namespaceHandler.init();               //缓存下               handlerMappings.put(namespaceUri, namespaceHandler);               return namespaceHandler;           }           catch (ClassNotFoundException ex) {               throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +                       namespaceUri + "] not found", ex);           }           catch (LinkageError err) {               throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +                       namespaceUri + "]: problem with handler class file or dependent class", err);           }       }   }



限于篇幅太长,关于bean解析以及custom的具体解析未来会分条详细介绍,敬请期待



下节预告


Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: