目录
4.2、parseBeanDefinitionElement方法
4.3、parseBeanDefinitionElement()方法
在上片博文spring容器获取bean实例流程 我们大致分析了如何将xml转换为Document,对应的document转换为BeanDefinition并注册的解析逻辑,但是关于标签的详细解析留在这个环节去讲解,首先根据上面的代码分析我们可以清楚的知道有关spring标签的解析分为两类,
- 默认标签的解析,例如:
<bean id="son" class="" >
<alias />
- 自定义标签的解析
<tx:advice />
<aop:config/>
默认标签解析分析
默认标签分为四种import标签,alias标签,bean标签,beans标签 四种类似,spring源码中分别对于这四种默认标签提供了对应的四个方法分别进行解析对应的标签。
//对Spring beans xml 配置的默认标签进行解析 默认标签 包含几个大类
// 1、import导入别的xml配置
// 2、alias显示注册别名
// 3、bean 对应配置的实例bean解析 是我们最常用同时也是最重要的解析方法(主要探究点)
// 4、beans 即<beans /> 标签下包含一个内部的<beans /> (本地使用递归)
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果对应的doc节点是import 走import标签解析逻辑
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果对应的doc节点是alias 走注册别名的逻辑
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//如果对应的doc节点是bean 走注册解析bean标签并转换为BeanDefinition对象的逻辑
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//内部beans 标签解析 再次调用该类的doRegisterBeanDefinitions()
//把其当做一个新的beans.xml直接进行解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse 递归执行
doRegisterBeanDefinitions(ele);
}
}
1、import标签解析
import标签可以使我们的spring配置结构清晰 对于比较有着庞大配置的项目,我们可以将具体的某个相似的功能bean配置 分模块的配置在单独的xml配置文件中,比如数据源相关的配置spring-datasource.xml、缓存相关的配置 spring-cache.xml ,在最终的配置xml文件中使用import的方式将其引入 条理清晰 可读性也好。
//import标签解析的具体逻辑
protected void importBeanDefinitionResource(Element ele) {
//获取import标签的resource属性 该属性表示为一个新的资源文件路径
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//对于resource属性如果有类似${user.dir}占位符的使用该方法调用
//将占位符转换为真实的location值
location = getReaderContext().getEnvironment().
resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// Discover whether the location is an absolute or relative URI
boolean absoluteLocation = false;
try {
//如果location是URL或者绝对的uri
//(意思是表明该资源绝对路径可以被识别能访问到真实的资源)
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*:"
}
//资源是绝对路径
if (absoluteLocation) {
try {
//最终还是调用我们前面追踪到的XmlBeanDefintion的loadBeanDefinitions()方法()
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 {
// 没有绝对路径 则根据location获取相对资源 如果资源存在直接调用如上相同的方法
// 资源不存在 获取其根路径+location(相对路径)得到一个相对路径资源同样调用如上相同的方法
//其他情况 抛出异常信息
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[0]);
//触发事件监听器 通知import解析完成
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
2、alias标签解析
alias标签可以让我们显式为我们所定义的bean添加别名
//别名标签注册 在进行bean定义的时候 除了使用id name定义名字外 还可以使用<alias />指定别名
protected void processAliasRegistration(Element ele) {
//获取alias标签的name属性 beanName
//(该属性值和配置中的某个bean的id或者name有直接或者间接的联系
//即beanName最终会指向配置中的某个bean)
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取alias标签的alias属性别名
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;
}
// name属性 和alias属性存在
if (valid) {
try {
//获取注册别名的类进行别名注册 默认实现类SimpleAliasRegistry
//使用map来存储name和alias的关联关系
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
//触发事件监听器 通知alias解析完成
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
3、beans标签的解析
beans.xml中嵌入式的beans标签,一般很少使用,但是非常类似于import标签的功能,本质上是将其当做beans标签递归调用解析beans的方法进行解析。此处不再进行描述。
4、bean标签的解析
beans标签是我们最常用同时也是spring最核心的解析逻辑,spring用了大量的篇幅来解析相关的bean标签,接下来让我们一步一步分析一下bean标签的解析流程
4.1、processBeanDefinition方法
//最常见的最核心最终的bean标签的解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean标签 将其转换为BeanDefinition 保存在BeanDefinitionHolder中
//(BeanDefinitionHolder中 持有一个BeanDefinition对象)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//当对应的BeanDefinitionHolder存在的时候 如果bean标签中有我们自定义的属性
//或者自定义的子节点标签
//对其自定义标签或者属性进行解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//注册最终解析并修饰后的BeanDefinition实例
//经过上面两部的操作得到的BeanDefinition对象 已经满足后续的使用要求了
// 接下来剩下的工作是注册对应的bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '"
bdHolder.getBeanName() + "'", ele, ex);
}
//发送注册bean的事件监听 通知该对应的bean定义已经注册成功
getReaderContext().fireComponentRegistered(
new BeanComponentDefinition(bdHolder));
}
}
下面我们分析一下这个方法主要逻辑:
- 委托BeanDefinitionParserDelegate 调用其parseBeanDefinitionElement()方法对bean的标签进行解析
- BeanDefinitionHolder如果存在且其中我们自定义的属性 或者自定义的子节点标签 再次对自定义标签进行解析
- 经过上面两部的操作得到的BeanDefinition对象 已经满足后续的使用要求了 接下来剩下的工作是注册BeanDefinition对象
- 最后触发响应事件,通知相关的监视器告知其对应的beanDefinition对象加载完成
4.2、parseBeanDefinitionElement方法
该方法主要是将bean的相关属性比如 id,class parent等进行解析填充到BeanDefintion实例并将其放BeanDefinitionHolder中。
//解析Bean
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//获取bean标签的id 和name属性 name属性根据",;"将其转换为数组存放在list中
//如果没有id 则别名的一个座位其唯一标识beanName(也是bean的原始名字)
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
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");
}
}
if (containingBean == null) {
//检查beanName是否已经被使用 没有被使用 将beanName和aliases 存放到usedNames列表中
checkNameUniqueness(beanName, aliases, ele);
}
//前面只是针对id和name属性进行解析获取别名的处理 该行代码对bean标签的其他属性进行解析并初始化BeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//如果beanName不存在则使用spring中提供的命名规则生成对应的beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
//如果有可能的话为该beanDefinition再注册一个别名 别名为beanClassName
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 根据beanDefintion对象、beanName,别名列表 创建BeanDefinitionHolder 并返回
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
下面我们分析一下这个方法主要逻辑:
- 解析bean的id,name属性转换为对应的beanName属性和别名alias列表
- 调用parseBeanDefinitionElement()方法对除了已经解析的id,name属性其他属性进行解析并填充BeanDefinition对象
- 如果beanName不存在则根据不同的规则生成对应的beanName
- 将对应的beanDefintion属性、beanName、alias作为参数创建BeanDefintionHolder对象并返回。
4.3、parseBeanDefinitionElement()方法
该方法对bean标签的除了id,name的其他属性进行解析,并将对应的属性值填充到BeanDefintion对象中。
//对处理别名(bean标签的id属性和name属性)之外的其他标签进行解析处理
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//在parseState中存放一个beanName对应的BenEntry对象 表明该bean实例正在被解析为BeanDefinition
//解析完成后该beanName对应的BenEntry对象应该会被移除
this.parseState.push(new BeanEntry(beanName));
//获取class属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//获取该实例需要继承的父类对象的bean(父类Bean的id 或者beanName)
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//初始化一个AbstractBeanDefinition对象并设置属性beanClass 和parentName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//设置Scope Abstract lazyInit、autowire、depends-on、autowire-candidate
// primary、init-method、destroy-method、factory-method、factory-bean等属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//设置beanDefinition的描述信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析bean标签的子<meta />标签
parseMetaElements(ele, bd);
//解析bean标签的lookup-method子标签
//将其转换为LookUpMethodOverride属性添加到BeanDefinition
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析bean标签的replaced-method子标签
//将其转换为ReplaceOverride属性添加到BeanDefinition
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析bean标签的constructor-arg子标签
parseConstructorArgElements(ele, bd);
//解析bean标签的property子标签
parsePropertyElements(ele, bd);
//解析bean标签的Qualifier子标签
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
//移除解析标记
this.parseState.pop();
}
return null;
}
spring针对bean标签的解析分成多个方法不同的方法对bean的不同属性或者子标签进行解析。
该方法大致逻辑分为:
- 构造 AbstractBeanDefinition 对象实例
- 解析primary、init-method等基础属性填充构造的AbstractBeanDefinition 实例
- 解析bean标签的子<meta />标签
- 解析bean标签的子<lookup-method/>标签
- 解析bean标签的replaced-method子标签
- 解析bean标签的constructor-arg子标签
- 解析bean标签的property子标签
- 解析bean标签的Qualifier子标签
针对上面的罗列的不同解析方式,我们这里重点讨论3、4、5、6、7相关的解析方法,其他的比较简单,这里不讲述。
4.4、解析bean标签的子<meta />标签
AbstractBeanDefintion本身继承了BeanMetadataAttributeAccessor类,将meta 的key和value值转换为BeanMetadataAttribute属性添加到attributeAccessor中,因为BeanMetadataAttributeAccessor类实现了BeanMetadataElement,所有可以通过addMetadataAttribute()方法添加meta标签解析出来后的属性信息,在这里同时设置source。
//对meta标签进行解析
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//获取bean标签的子元素 进行遍历
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果某个子元素是meta标签元素 <meta key="key" value="values" />
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
//获取meta标签的key和value 将其包装成BeanMetadataAttribute对象
//设置到BeanDefintion对象中 BeanDefintion是BeanMetadataAttributeAccessor的子类
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
4.5、lookup-method标签解析
解析lookup-method和解析meta标签大同小异,注释将对meta节点的判断变为对lookup-method节点的判断,解析出来的属性信息是创建了一个MethodOverride的子类LookupOverride对象来接受,并将其添加到BeanDefintion对象的methodOverrides属性中。
有关lookup-method的使用请参考:spring源码分析之BeanDefinition 小结 3.5、look-up属性
//解析bean下的lookup-method 子标签
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
//获取bean标签的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
//遍历子标签中是否有lookup-method标签
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
//有的话 获取其name和bean属性包装成LookupOverride
//设置到BeanDefinition的MethodOverrides中
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
4.6、replaced-method子标签解析
解析replaced-method和解析lookup-method标签大同小异,注释将对lookup-method节点的判断变为对replaced-method节点的判断,解析出来的属性信息是包装为ReplaceOverride对象,并将其添加到BeanDefintion对象的methodOverrides属性中。
//解析bean下的replaced-method 子标签
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//遍历子标签replaced-method
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
//获取replaced-method 的name、bean 包装为ReplaceOverride
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
//replaced-method标签中有对应的arg-type 针对name匹配对应的name对应方法中参数
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
4.7、解析 constructor-arg 标签
对构造函数的解析是非常常用也是非常复杂的,参考到meta、look-up、 replaced-method等元素的解析,我们很容易猜想到对constructor-arg解析方式 遍历bean标签的子节点,判断子节点是否为 constructor-arg 标签,如果是解析 constructor-arg 标签将属性包装成类似的对象填充到BeanDefintion中。
//解析bean的字标签的constructor-arg字标签
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
//遍历构造器参数解析bean的字标签的constructor-arg字标签
parseConstructorArgElement((Element) node, bd);
}
}
}
通过代码我们可以看出一切和我们分析的逻辑大体一致,只是在spring中对constructor-arg 标签的解析放到了方法parseConstructorArgElement()中(可能是比较复杂的缘故)下面我们看看该方法做了什么
//具体解析对应的 constructor-arg 标签
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
//获取 constructor-arg 的index,type,name
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//如果有index属性 构造器参数的解析
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
//标记正在解析bean标签的constructor-arg 子标签
this.parseState.push(new ConstructorArgumentEntry(index));
//解析子标签对应的对象value 从constructor-arg 的 ref value
// 或者子标签对应的value值
Object value = parsePropertyValue(ele, bd, null);
//构造ConstructorArgumentValues constructor-arg 标签的属性包装类
ConstructorArgumentValues.ValueHolder valueHolder = new
ConstructorArgumentValues.ValueHolder(value);
//设置构造器参数对象的type和name
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//如果beanDefinition中的构造器参数对象中已经存在该index 抛出异常
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index))
error("Ambiguous constructor-arg entries for index "
+ index, ele);
}
else {
//设置构造器参数信息 constructor-arg有多个
//存放在ConstructorArgumentValues的
//indexedArgumentValues中map集合中
bd.getConstructorArgumentValues()
.addIndexedArgumentValue(index, valueHolder);
}
}
finally {
//移除标记
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
try {
//如果没有index属性 构造参数解析
this.parseState.push(new ConstructorArgumentEntry());
//解析子标签对应的对象value 从constructor-arg 的 ref value
//或者子标签对应的value值
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder =
new ConstructorArgumentValues.ValueHolder(value);
//设置构造器参数对象的type和name
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//添加构造器参数 存放在ConstructorArgumentValues的
//genericArgumentValues中list集合
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
代码有点多这里我们简单罗列一下大致流程
1、首先获取对constructor-arg 标签的index、name、type属性
2、根据有无index分成两个方式处理处理
两种处理方式大致只是构造器参数 有无index 存放的位置不一致。
- 有index的构造器参数对象存放ConstructorArgumentValuesindexedArgumentValues中,
- 无index的构造器参数对象存放ConstructorArgumentValues的genericArgumentValues中。
3、解析constructor-arg 标签 构造ConstructorArgumentValues的ValueHolder来封装解析出来的元素
封装成ValueHolder对象的属性是通过该parsePropertyValue方法和之前解析出来的name和type属性
我们来探究一个parsePropertyValue()方法
//解析property标签或者constructor-arg 的三种类型的ref属性 vlaue属性 子标签
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
//获取子标签 类似于ref value属性 list 等子标签
//ref获取引用类型的数据 value 基本类型的数据 list则为constructor-arg或者property子标签
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
//ref、value、和subElement只能存在一个
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value'
attribute OR sub-element", ele);
}
//获取ref对应的数据
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
//获取value对应的数据
else if (hasValueAttribute) {
TypedStringValue valueHolder = new
TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//对于子标签需要重新进行解析
else if (subElement != null) {
/**
* 解析constructor-arg、property的子标签 比如bean标签 ref标签
* parent标签 idref标签 value标签
* map标签 list标签 array标签等 具体的标签解析 调用不同的方法 这里不再往下分析
* <constructor-arg>
* <bean></bean>
* <idRef></idRef>
* </constructor-arg>
*/
return parsePropertySubElement(subElement, bd);
}
//没有对应的属性信息返回null
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
该方法主要针对property标签或者constructor-arg 的三种类型的ref属性 vlaue属性 子标签三种类型的标签属性进行解析
首先三者属性只能存在一个, ref属性会封装成RuntimeBeanReference,value属性会封装为TypedStringValue,子标签会调用parsePropertySubElement()方法进行子标签bean标签、ref标签、 parent标签、 idref标签、 value标签、 map标签 、list标签 array标签进行解析 不同的解析调用不同的方法。
4.8、解析property子标签
property子标签解析的方式和onstructor-arg方式相同,获取bean标签的所有子节点 遍历子节点如果是property标签则解析该标签信息封装到PropertyValue对象中填充到BeanDefinition对象中,同时property标签下可能有meta标签 会同时解析meta标签解析方式和上面4.4方式相同
// 获取bean标签下的所有子<propert/> 依次调用parsePropertyElement方法
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
//具体解析<property /> 放到该方法下
parsePropertyElement((Element) node, bd);
}
}
}
//解析<property />标签
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取property 的name属性值
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
//标记解析状态PropertyEntry属性
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果BeanDefinition中已经有了与propertyName相匹配的属性则返回
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" +
propertyName + "'", ele);
return;
}
//解析元素中的value值并将其包装为PropertyValue对象 设置到bd中
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
//解析peoperty属性下的meta标签 如果有的话
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}