开篇
上一篇讲解了 Spring 中的标签包含自定义标签和默认标签,这两种方式存在较大不同,所以本文主要讲解默认标签的解析过程。
默认标签的解析是在 parseDefaultElement 方法中。
该方法分别对不同标签做不同处理。
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)) {
doRegisterBeanDefinitions(ele);
}
}
Bean 标签的解析及注册
这四种中,我们主要关注对 bean 标签的解析。bean 标签的解析是最复杂且重要的。我们进入 processBeanDefinition 方法。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
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));
}
}
该段代码我们还是先看时序图。
该方法 processBeanDefinition 大致逻辑如下:
- 首先调用了
delegate.parseBeanDefinitionElement(ele)
方法进行元素解析。并返回 BeanDefinitionHolder 类型的 bdHolder,经过这个方法后,bdHolder 实例中已经包含了配置文件中的各种属性,比如 class,name,id,alias 等 - 当返回的 bdHolder 不为空的情况下,若存在默认标签的子节点下还有自定义属性,还要对自定义标签进行解析。
- 解析完成后,需要对解析后的 bdHolder 进行注册,注册操作委托给了
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
方法 - 最后发出响应事件,通知相关监听器,该 bean 已经加载完成
解析 BeanDefinition
接下来我们一点点分析,首先我们分析该方法delegate.parseBeanDefinitionElement(ele)
。
该方法在BeanDefinitionParserDelegate类中。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分割name属性
List 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.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 代码(1)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
//如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
该方法就是对默认标签解析的全过程,我们现在可以可以看到对属性 id、name 的解析。
在当前方法主要完成的内容如下:
- 提取元素 id、name 属性
- 解析其他属性并封装到 GenericBeanDefinition 类型实例中
- 如果检测到 bean 没有指定 beanName,则使用默认规则生成一个 beanName
- 将获取到的信息封装到 BeanDefinitionHolder 实例中
我们看一下代码中标注的代码(1)调用的parseBeanDefinitionElement
方法是如何对其他标签进行解析的。
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//代码(1)创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//代码(2)解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//代码(3)解析元数据
parseMetaElements(ele, bd);
//解析lookup-medthod属性 (用的很少,这里就不深入介绍)
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replace-medthod属性(用的很少,这里就不深入介绍)
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//代码(4)解析构造函数参数
parseConstructorArgElements(ele, bd);
//代码(5)解析property子元素
parsePropertyElements(ele, bd);
//解析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;
}
创建用于属性承载的 BeanDefinition
我们先看一下代码(1)调用的方法之前,我们先再了解一下 BeanDefinition。
BeanDefinition 是一个接口,在 Spring 中存在三种实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。它们均继承自AbstractBeanDefinition,其中 BeanDefinition 是配置文件
元素在容器内部的表现形式。该标签拥有 class、scope、lazy-init 等配置属性,BeanDefinition 也提供了对应的属性:beanClass、scope、lazyInit。
其中 RootBeanDefinition 是最常用的实现类,一般对应
元素标签,而 GenericBeanDefinition 是 2.5 版本后加入的 bean 文件配置属性定义类,提供一站式服务类。
在配置文件中我们可以父和子
,父就用 RootBeanDefinition 表示,而子就使用 ChildBeanDefinition 表示。普通的
就使用 RootBeanDefinition 来表示,AbstractBeanDefinition 则对两者共同类的信息进行抽象。
Spring 通过 BeanDefinition 将配置文件的
转换为容器内部表示,并且将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。
Spring 容器的 BeanDefinitionRegistry 主要以 map 形式存储,后续操作可以直接从该类中获取配置信息。
但首先,我们解析属性之前就需要创建用于承载属性的实例,也就是创建了我们之前说的 GenericBeanDefinition 类型的实例。也就是代码(1)调用的createBeanDefinition(className, parent)
方法。
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
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) {
//如果classLoader不为空则使用传入的classLoader进行加载类对象,否则只是记录className
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}else {
bd.setBeanClassName(className);
}
}
return bd;
}
至此,我们就创建好了 GenericBeanDefinition 实例。
解析各种属性
当创建完用来承载 Bean 信息的 GenericBeanDefinition 实例后,就可以对 bean 信息的各种属性进行解析了。
首先我们进入代码(2)parseBeanDefinitionAttributes
方法,该方法对 element 所有元素属性进行解析。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析singleton属性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton属性已经不被支持,使用scope代替
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
//解析scope属性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
//在嵌入BeanDefinition情况下,并且没有单独指定scope属性,则使用父类默认的属性
bd.setScope(containingBean.getScope());
}
//解析abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//解析lazy-init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析depends-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析autowire-candidate属性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//解析init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析destroy-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
该方法主要做的就是拿到各种属性对应的属性值放入 AbstractBeanDefinition 对应属性中。
解析子元素 meta
首先我们回顾一下如何使用 meta 属性。
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
这段代码并没有体现在 MyTestBean 中,只是一个声明,在使用的时候可以使用BeanDefinition类的getAttribute(key)
方法获取。
接下来我们看一下是如何解析的,进入代码(3)。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//获取当前节点所有元素
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//判断节点是否为meta
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//构造BeanMetadataAttribute实例
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
//记录信息
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
解析子元素 constructor-arg
对构造函数的解析还是非常常见的,同时也是很复杂,举个例子:
Jack
hello
该代码就是 Spring 中最基础的配置,自动寻找对应的构造器并在初始化的时候将设置的参数传入进去,接下来看一下如何解析。
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
//拿到bean所有子节点
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
方法。
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));
//代码(1)解析ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
//使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
//将name属性和type都封装到valueHolder中
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 {
//添加到BeanDefinition的ConstructorArgumentValues中,存入结构为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 {
//index为空的处理
try {
this.parseState.push(new ConstructorArgumentEntry());
//解析ele节点对应的属性值
Object value = parsePropertyValue(ele, bd, null);
//使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
//将name属性和type都封装到valueHolder中
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//添加到BeanDefinition的ConstructorArgumentValues中,因为没有index则存入结构为List
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
该方法并不是特别复杂,首先提取 constructor-arg 上必要的属性(index、type、name)。
配置中指定了 index 的话操作步骤如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 类型封装解析后的元素
- 最后将 index、name、type 封装到 ValueHolder 类型中,并添加到 BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 属性中。
配置中没有指定 index 的话操作步骤如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 类型封装解析后的元素
- 最后将 index、name、type 封装到 ValueHolder 类型中,并添加到 BeanDefinition 的 constructorArgumentValues 的 genericArgumentValues 属性中。
了解完流程之后,我们看一下具体是如何进行解析的,进入代码(1)parsePropertyValue
的方法中。
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
" element for property '" + propertyName + "'" :
" element");
// 获取ele节点的子节点,一个属性只能对应一种类型:ref/value/list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//跳过meta节点或description节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
//只能有一个子节点,否则异常
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
//把子节点赋值给subElement
subElement = (Element) node;
}
}
}
//解析constructor-arg的ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//解析constructor-arg的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属性的处理,使用TypedStringValue封装
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
//解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
//如果没有ref和value,也没有子元素则抛出异常
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
该方法对构造函数中属性的元素解析,经过以下过程:
- 跳过 description 或者 meta
- 提取 constructor-arg 上的 ref 和 value 属性,随后进行校验
ref 属性处理,使用 RuntimeBeanReference 封装对应的 ref 名称,比如:
value 属性的处理,使用 TypedStringValue 封装,比如:
子元素处理,比如:
对于子元素的处理,比如这里提到的加入了 map 元素,是如何处理的?具体在parsePropertySubElement
中实现了各种子元素的处理。
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
//判断是否为默认命名空间,如果不是就进行解析自定义节点
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//解析是否为bean节点
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//解析ref标签
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
//解析parent
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error(" element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
//解析idref元素
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//解析value元素
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//解析null元素
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//解析array元素
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//解析list元素
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//解析set元素
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//解析map元素
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//解析props元素
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
在该方法中实现了所有支持的类型的分类处理,到此就已经理清楚构造函数是如何解析了,这里就不深入研究如何解析 list、map 等元素了。
解析子元素 property
在分析完构造函数后,我们可以接着往下看,这里避免忘记我们再看一下目前到哪里了。
到这里我们先回顾一下如何使用 property 属性。当然,property 属性里也可以使用 list 等类型的元素。
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
接下来我们看一下是如何解析的。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取到beanElement的所有子节点
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);
}
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取配置元素的name值
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
//name为空则抛出异常
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//校验在相同bean节点下,是否存在同样的name属性,如果存在则抛出异常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析属性值
Object val = parsePropertyValue(ele, bd, propertyName);
//解析后的值和name属性封装为PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
//解析meta节点
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
//解析完成后添加到BeanDefinition的propertyValues属性中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
和之前讲解的过程都差不多,都是先获取所有子标签然后进行遍历进行解析,获取对应的 name、value 值进行封装。
解析子元素 qualifier
该元素我们一般使用注解偏多,主要就是当接口存在多个实现类时候,在我们注入时指定某一个实现类,这样 Spring 容器就可以找到对应的 bean。因为在 Spring 中候选的 Bean 数目必须有且仅有一个。解析过程和之前都差不多,这里就不再赘述。
AbstractBeanDefinition 属性
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
至此,我们就完成了对 XML 文档到 GenericBeanDefinition 的转换,XML 中的配置都可以在 GenericBeanDefinition 中看到,但 GenericBeanDefinition 只是子类,大部分属性都在 AbstractBeanDefinition 中。我们回顾一下都有哪些配置。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// 此处省略静态变量以及final变量
@Nullable
private volatile Object beanClass;
/**
* bean的作用范围,对应bean属性scope
*/
@Nullable
private String scope = SCOPE_DEFAULT;
/**
* 是否是抽象,对应bean属性abstract
*/
private boolean abstractFlag = false;
/**
* 是否延迟加载,对应bean属性lazy-init
*/
private boolean lazyInit = false;
/**
* 自动注入模式,对应bean属性autowire
*/
private int autowireMode = AUTOWIRE_NO;
/**
* 依赖检查,Spring 3.0后弃用这个属性
*/
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
/**
* 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on
*/
@Nullable
private String[] dependsOn;
/**
* autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
* 将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,
* 但是该bean本身还是可以使用自动装配来注入其他bean的
*/
private boolean autowireCandidate = true;
/**
* 自动装配时出现多个bean候选者时,将作为首选者,对应bean属性primary
*/
private boolean primary = false;
/**
* 用于记录Qualifier,对应子元素qualifier
*/
private final Map qualifiers = new LinkedHashMap<>(0);
@Nullable
private Supplier> instanceSupplier;
/**
* 允许访问非公开的构造器和方法,程序设置
*/
private boolean nonPublicAccessAllowed = true;
/**
* 是否以一种宽松的模式解析构造函数,默认为true,
* 如果为false,则在以下情况
* interface ITest{}
* class ITestImpl implements ITest{};
* class Main {
* Main(ITest i) {}
* Main(ITestImpl i) {}
* }
* 抛出异常,因为Spring无法准确定位哪个构造函数程序设置
*/
private boolean lenientConstructorResolution = true;
/**
* 对应bean属性factory-bean,用法:
*
*
*/
@Nullable
private String factoryBeanName;
/**
* 对应bean属性factory-method
*/
@Nullable
private String factoryMethodName;
/**
* 记录构造函数注入属性,对应bean属性constructor-arg
*/
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
/**
* 普通属性集合
*/
@Nullable
private MutablePropertyValues propertyValues;
/**
* 方法重写的持有者,记录lookup-method、replaced-method元素
*/
@Nullable
private MethodOverrides methodOverrides;
/**
* 初始化方法,对应bean属性init-method
*/
@Nullable
private String initMethodName;
/**
* 销毁方法,对应bean属性destroy-method
*/
@Nullable
private String destroyMethodName;
/**
* 是否执行init-method,程序设置
*/
private boolean enforceInitMethod = true;
/**
* 是否执行destroy-method,程序设置
*/
private boolean enforceDestroyMethod = true;
/**
* 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置
*/
private boolean synthetic = false;
/**
* 定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关,
* SUPPORT:某些复杂配置的一部分
* 程序设置
*/
private int role = BeanDefinition.ROLE_APPLICATION;
/**
* bean的描述信息
*/
@Nullable
private String description;
/**
* 这个bean定义的资源
*/
@Nullable
private Resource resource;
}
解析默认标签中的自定义标签元素
到目前为止,我们已经分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
这行代码,接下来我们继续分析bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
这行代码。
我们先了解一下这行代码的大概作用,从语义上来分析:如果需要的话就对 BeanDefinition 进行装饰,类似于如下场景:
当 Spring 中的 bean 使用的是默认标签配置,但是子元素却使用自定义配置的时候,这行代码就会执行。
但是为什么会在默认类型解析中单独添加一个自定义类型呢?首先这个自定义类型并不是以 bean 的形式出现的,在这里的自定义类型其实相当于是属性。
我们继续分析该方法代码。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
在调用decorateBeanDefinitionIfRequired
方法时,第三个参数传入为 null,该参数是父类 bean,当对某个嵌套配置分析时需要传递父类的 BeanDefinition,其实就是为了使用父类的 scope 属性,如果子类没有设置 scope 属性则使用父类的 scope 属性。这里是顶层配置,所以传递为 null。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// 遍历节点,查看是否存在适用于装饰的属性
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
//遍历子节点,查看是否存在适用于装饰的属性
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
最终都调用到了decorateIfRequired
方法,我们进入此方法查看。
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//获取自定义命名空间
String namespaceUri = getNamespaceURI(node);
// 过滤默认命名空间
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
//根据命名空间找到相应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
//进行装饰处理
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
到这里已经很明确了,首先获取元素或者属性的命名空间,然后判断是否适用于自定义标签的解析条件,随后找到对应的 NamespaceHandler 进行下一步解析,该部分会在自定义标签解析中讲解。
总结:该方法的作用就是对自定义标签或者自定义属性进行处理,然后找到对应的命名空间处理器进行进一步的解析。
注册解析的 BeanDefinition
到这里,我们对配置文件的解析、装饰都已经完成,现在的 BeanDefinition 已经满足使用要求了,后续就剩下了注册工作。
也就是 processBeanDefinition 方法中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
这行代码。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//获取beanName做唯一标识注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果有别名的话,注册所有别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
该方法获取到 beanName 后,最终 BeanDefinition 都会注册到BeanDefinitionRegistry中,该方法分为两部分,一种为 beanName 注册方式和别名注册方式。
对于 BeanDefinition 的注册,不仅仅是将 BeanDefinition 放入 map 中,然后 beanName 作为 key。除此之外还做了别的事情。
进入 DefaultListableBeanFactory 类实现中。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//注册前最后一次校验,针对AbstractBeanDefinition中的methodOverrides校验
//校验methodOverrides是否与工厂方法并存,或者methodOverrides对应的方法压根不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//判断是否已经存在bean
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果对应的beanName已经注册过并且不允许覆盖,则抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//是否已经开始创建bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 因为beanDefinitionMap是全局变量,这里会存在并发访问的情况
synchronized (this.beanDefinitionMap) {
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
//记录beanName
this.beanDefinitionNames.add(beanName);
//从factoryBeanCreatedCache中移除掉这个beanName
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有beanName对应的缓存
resetBeanDefinition(beanName);
}
}
注册 bean 分为以下四步:
- 对 AbstractBeanDefinition 的 methodOverrides 属性校验
- 如果已经注册过并且不允许覆盖则抛出异常,否则直接覆盖
- 加入 map 缓存
- 清除解析前的 beanName 缓存
之后我们再看通过别名注册就简单多了。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
//如果beanName与alias相同则不记录alias,并删除对应的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
//不允许覆盖则抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
//确保添加的没有name和alias值相反的数据且alias和name不相等
checkForAliasCircle(name, alias);
//存入map中
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
从该方法可知,注册 alias 步骤如下:
- alias 和 beanName 相同情况处理,如果相同则不需要处理并删除原有 alias
- 覆盖校验处理
- alias 循环检查
- 注册 alias
通知监听器解析及注册完成
在注册完成后,当开发人员需要对注册 BeanDefinition 事件进行监听时可以通过注册监听器方式将处理逻辑写入监听器中,在 Spring 中并没有对此事件做任何逻辑处理。
总结
到这里,Bean 的解析和注册过程已经全部 OK 了。
回顾一下,解析 BeanDefinition 的入口在 DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()
。该方法会根据命令空间来判断标签是默认标签还是自定义标签,其中默认标签由 parseDefaultElement()
实现,自定义标签由 parseCustomElement()
实现。在默认标签解析中,会根据标签名称的不同进行 import 、alias 、bean 、beans 四大标签进行处理,其中 bean 标签的解析为核心,它由 processBeanDefinition()
方法实现。processBeanDefinition()
开始进入解析核心工作,分为三步:
- 解析默认标签:
BeanDefinitionParserDelegate.parseBeanDefinitionElement()
- 解析默认标签下的自定义标签:
BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired()
- 注册解析的 BeanDefinition:
BeanDefinitionReaderUtils.registerBeanDefinition()
在默认标签解析过程中,核心工作由 parseBeanDefinitionElement()
方法实现,该方法会依次解析 Bean 标签的属性、各个子元素,解析完成后返回一个 GenericBeanDefinition 实例对象。
最后通过registerBeanDefinition方法进行对BeanDefinition进行注册后就大功告成了。