默认标签的解析
前面一章(点击查看)说了spring使用了俩种标签解析方式,下面这一章着重介绍一下spring的标签解析
首先查看spring默认的解析方法,点击进入方法1 (parseDefaultElement)
DefaultBeanDefinitionDocumentReader.java
/**
* 解析默认标签
* 4种情况,import、alias、bean、beans
* @param ele
* @param delegate
*/
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);
}
}
我们先看关于bean的解析,即我们进入解析bean的方法processBeanDefinition(ele,delegate)
DefaultBeanDefinitionDocumentReader.java
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* 1 委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,此时bdHolder已经包含配置文件
* 中的属性,如class,name,id
* 2 当bdHolder不为空时,如存在默认标签的子节点下在有自定义属性,还需要再次解析
* 3 解析完成,进行bdHolder进行注册
* 4 响应事件,通知监听器,完成bean加载
*/
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);
}
// 发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
上述代码可知,我们首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement对元素进行的解析,点进去,查看parseBeanDefibitionElement的方法
可以看出parseBeanDefinitionElement方法又调用了BeanDefinitionDelegate类里面的带有两个参数的parseBeanDefinitionElement,我们看一下
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 如果name不为空 将name分割
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// id作为Bean的名称
String beanName = id;
// 别名冲突
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 假如id = null 别名不为空 则取首个别名作为beanName list.remove 会返回被移除的那个元素
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 判断在当前Bean标签中 是否存在相同的名称
checkNameUniqueness(beanName, aliases, ele);
}
// 调用重载的方法进行解析
//主线
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果不存在beanName 那么根据Spring 默认命名规则生成
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
//假如类名称未使用,则将类名作为当前bean的别名
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//将BeanDefinition封装至BeanDefinitionHolder中,并返回
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
我们这一步的containingBean默认是null,所以进入checkNameUniqueness(beanName, aliases, ele)我们看一下他怎么实现的
/**
* Validate that the specified bean name and aliases have not been used already
* within the current level of beans element nesting.
* 检查当前beanName aliases ... 是否是唯一的
*/
protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
String foundName = null;
// usedNames为一个hashSet
if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
foundName = beanName;
}
if (foundName == null) {
foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
}
if (foundName != null) {
error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
}
this.usedNames.add(beanName);
this.usedNames.addAll(aliases);
}
这里我们主线方法是调用的parseBeanDefinitionElement,带有三个参数的重载方法,我们进去看一下具体实现
BeanDefinitionParserDelegate.java
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//将当前bean解析入栈,记录解析<bean>的过程,parseState可以通过toString来打印调用栈链
//下面解析property的时候,也同样记录了
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//根据类名和父类名创建BeanDefinition AbatractBeanDefinition几乎包含了bean实例化的所有信息
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析DOM中的属性,并存入到AbstractBeanDefinition对象中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析子元素Mate
parseMetaElements(ele, bd);
// 解析lookup-method 属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析子元素replace-method 可以动态的替换返回的实体Bean 更改原方法的逻辑
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析子元素 constructor-arg
parseConstructorArgElements(ele, bd);
// 解析property
parsePropertyElements(ele, bd);
// 解析qualifierS
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的parent属性代表子类继承了父类,会生成具体实例,在子类Bean中配置会覆盖父类对应的属性。
我们继续观察一个createBeanDefinition代码
图中我们可以看到spring利用当前类名和当前父类名返回了一个AbstractBeanDefinition
创建用于属性承载的Bean
在上面说到了AbstractBeanDefinition,AbstractBeanDefinition其实相对于一个pojo类,包括了实例化bean的大部分信息
AbstractBeanDefinition实现了BeanDefinition,那么什么是BeanDefinition???
BeanDefinition
BeanDefinition是一个接口,在Spring中存在三种实现分别是
- RootBeanDefinition
- ChildBeanDefinition
- GenericBeanDefinition
这三种实现都继承了AbstractDefinition,其中BeanDefinition是配置文件元素标签在容器中的内部表现形式,即元素标签拥有class,scope,lazy-init等等配置属性,BeanDefinition则提供了相应的beanClass,scope等等属性,其中最常用的还是RootBeanDefinition
其中在配置文件中还可以定义父 和子,父bean用RootBeanDefinition表示,而子bean用ChildDefinition表示
由此要想解析属性就必须创建承载属性的实例,也就是创建一个GenericBeanDefinition类型的代码,在parseBeanDefinitionElement就有这样一个方法,
点击进去
发现他调用的是utils里面的一个方法,返回了一个AbstractBeanDefinition
BeanDefinitionReaderUtils.java
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
//parentName有可能为空
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 如果classLoader不为空,则使用传入的classLoader同一虚拟机加载类对象,否则只是记录className
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
当创建了bean信息实载之后,就可以进行bean的各种属性的解析了,首先进入BeanDefinitionParserDelegate.java的parseBeanDefinitionAttribute方法里面
我们可以看到这里Spring完成了所有bean的解析,这些属性大部分是我们经常使用的
我们发现下面还调用的了这几种属性,我们分别看一下
标签1解析子元素meta
meta即元数据
<bean id="car" class="test.CarFactoryBean">
<property name="carInfo" value="超级跑车,400,2000000"/>
<meta key = "key" value = "values">
</bean>
这段代码并不会体现在 CarFactoryBean 的属性当中,而是一个额外的声明,当需要里面的属性时,可以通过BeanDefinition的getAttribute(key);方法获取,
spring 源码是这样的
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
// 获取当前节点下面的所有子元素
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 提取mate
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 使用key,value 构造BeanMetadataAttribute
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 记录信息
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
标签2解析子标签lookup-method
lookup-method获取机器注入,我感觉有一点像vue的solt插槽,《Spring in Action》里面是这样说的,获取器注入是一种特殊的方法的注入,它是把一个方法声明为返回某一种类型的bean,但是实际返回的bean是配置文件里面配置的
源码
/**
* Parse lookup-override sub-elements of the given bean element.
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 获取lookup-method 标签
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
// 获取要修饰的方法
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
// 获取配置返回的bean
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
标签2解析子元素replaced-method
可以改变某个方法甚至是改变方法的逻辑,即可以在运行时用新的方法替换到现有的方法
源码
/**
* Parse replaced-method sub-elements of the given bean element.
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
// 提前要替换的旧方法
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
// 提前对应替换的新方法
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
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);
}
}
}
标签3解析子元素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)) {
// 开始解析constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
我们可以看到关于构造函数的解析最终是靠parseConstructorArgElement这个方法的,点进去
/****对于是否指定了index,spring的处理流程是不一样的,关键是在于属性信息被保存的位置**
* Parse a constructor-arg element.
* 1,如果配置中指定了index属性
* (1) 解析constructor-arg的子元素
* (2) 使用ConstructorArgmentValues.ValueHolder类型来封装解析出来的元素
* (3) 将type,name和index属性一起封装在ConstructorArgumentValues.ValueHolder
* 类型中并添加至当前的BeanDefinition的constructorArgumentValues的indexedArgumentValues属性中
* 2 否则
* (1)解析constructor-arg的子元素
* (2)将ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
* (3)将type,name和index属性一起封装在ConstructorArgumentValues.ValueHolder
* 类型中并添加至当前的BeanDefinition的constructorArgumentValues的indexedArgumentValues属性中
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取index属性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
// 没有index 属性则忽略去属性自动去寻找
else {
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
看一下parsePropertyValue方法
/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 一个属性只能对应一种类型
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 对于description和meta不处理
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属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 解析value 属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 在constructor-arg上不存在 1同时即有ref属性又有value属性 2 存在ref属性或者value属性且又又子元素
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
// ref属性处理,使用RuntimeBeanReference 封装对应的ref 名称
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;
}
else if (hasValueAttribute) {
// value属性的处理 使用typedStaringValue
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// 对于各类子元素的分类处理
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
还剩下两个标签元素,分别是子元素property和qualifier
/**
* Parse property sub-elements of the given bean element.
*/
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)) {
parsePropertyElement((Element) node, bd);
}
}
}
这里主要是使用parsePropertElement((Element)node,bd)来进行解析,我们点击进去查看他的方法
/**
* Parse a property element.
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
一层层的进去
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 一个属性只能对应一种类型
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 对于description和meta不处理
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属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 解析value 属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 在constructor-arg上不存在 1同时即有ref属性又有value属性 2 存在ref属性或者value属性且又又子元素
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
// ref属性处理,使用RuntimeBeanReference 封装对应的ref 名称
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;
}
else if (hasValueAttribute) {
// value属性的处理 使用typedStaringValue
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// 对于各类子元素的分类处理
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
最后一个标签就留个大家自己解析了,下一次我们看一下默认标签中的自定义标签元素