上一篇中,我们已经找到了默认标签解析入口,也就是方法parseDefaultElement,下面我们接着从这个方法分析:
我们到方法parseDefaultElement中,看下Spring是如何解析默认标签的。
可以看到,解析xml标签的结构还是比较清晰的,我们先看下这几个常量表示的是什么?
这些不就是我们的标签名称吗,比如beans、import、alias。
因为我们平时使用alias 和import 标签都比较少,所以我们就不重点分析它们了,我们还是看比较核心的bean标签的解析。
我们跳到processBeanDefinition方法中:
可以看到 在这个方法中,主要是分为两个步骤:
1、解析bean的标签元素
2、将解析的bean注册到spring容器中
我们先看解析bean的标签元素,这个解析操作还是委托给BeanDefinitionParserDelegate来帮忙的。那么这个BeanDefinitionParserDelegate对象会如何解析默认标签bean呢?我们到这个delegate.pareseBeanDefinitionElement方法中去看:
我们继续跟到方法parseBeanDefinitionElement方法中看下:
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 1、获取bean标签中,属性id和name的值
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 2、将属性name的值,通过分隔符"," 进行切分,并将数据添加到aliases中
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//3、如果属性id的值为空,那就获取aliases 集合中的第一个value值,作为bean的名称
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);
}
//4、开始深入的解析bean标签,将解析的结果封装为AbstractBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// beanName 不等于空,就直接跳过
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
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)) {
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;
}
}
// 5、根据解析到的beanDefinition、beanName和aliases 创建一个BeanDefinitionHolder
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
里面bean标签的解析还是挺复杂的,接下来我们就要一步一步的分析下bean标签解析的各个环节。
我们先看这两行代码,首先是获取标签中的属性id值,以及属性name的nameAttr,我们在bean标签中都会配置属性id的。
接下来,如果name属性值nameAttr不为空的话,通过方法StringUtils.tokenizeToStringArray进行分割:
其中,MULTI_VALUE_ATTRIBUTE_DELIMITERS的值为",;",也就是说在配置属性name的值时,可以通过"," 或";" 作为分隔符,配置多个name属性值。比如name属性值为 “user,users”,分割后可以得到nameAttr数组为[“user”,“users”],然后将数组添加到aliases集合中。
那aliases集合有什么作用呢?我们接着继续看:
可以看到,当id属性值为空时,就会从aliases集合中调用方法remove(0),来获取第一个元素作为beanName的值。
上面的这些都是bean标签解析前的一些准备工作。
我们继续往下面看:
通过这个方法名称parseBeanDefinitionElement 我们就应该知道,bean标签解析的关键方法就是它了。我们在进到方法parseBeanDefinitionElement 中看下:
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 1、 如果bean标签存在class属性,获取class 属性值
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 2、如果bean标签存在parent属性,获取parent属性值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 3、通过属性class 和 parent值,创建AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 4、解析bean标签中的各种其他属性,并封装到AbstractBeanDefinition中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//5、解析bean标签下的各种子标签元素,解析结果封装到AbstractBeanDefinition中
// 解析bean的子标签元素 meta
parseMetaElements(ele, bd);
// 解析bean的子标签元素, lookup-method
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析bean的子标签元素 replaced-method
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));
// 返回bd对象
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;
}
可以看到,这个方法中还是继续解析bean标签中的属性,分别获取属性class的值className,以及属性parent的值parent,然后将这两个属性的值传入到方法createBeanDefinition中,创建一个AbstractBeanDefinition类型的对象bd,最后把解析bean标签的属性值都封装到AbstractBeanDefinition里面。
那么在这个createBeanDefinition方法中到底做了什么呢?我们们知道AbstractBeanDefinition是一个抽象类是无法创建对象的。我们继续进去看下:
可以看到,在createBeanDefinition 方法中,BeanDefinitionReaderUtils又调用方法createBeanDefinition 进一步处理,继续往下看:
可以看到,实际上是创建的BeanDefinition为GenericBeanDefinition,并且将parent和class 属性值设置到GenericBeanDefinition之后并返回。
那这个GenericBeanDefinitio这个类是什么呢?我们都知道bean在Spring容器中都是以BeanDefinition的形式存在的,也就是说我们在xml中配置的一个bean标签,在Spring容器中存在的形式就是BeanDefinition。其实不但是通过xml配置的bean,就是我们通过注解形式配置的bean,最终都会生成BeanDefinition 。
但是这个BeanDefinition 只是一个接口而已,Spring在解析bean标签时会为我们创建一个GenericBeanDefinition出来,用于存放bean标签解析出来各种信息,我们在下一篇中将会介绍GenericBeanDefinition这个类。
Spring流程图: