Spring(四):bean 标签解析
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)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
对于 nodeNameEquals 方法,可以看到传进去的,参数都是 Element 对象和标签名称
而 delegate.nodeNameEquals 是判断传进来的标签的!下面就来看看他是如何判断的
因为对于默认的命名空间来说,其仅仅支持这 4 种标签(前面使用 parseDefaultElement 方法都是先判断命名空间的)
nodeNameEquals 方法(判断当前的标签是什么标签)
下面就来看看是怎么判断的
判断方法很简单
判断标签的名字是不是对应名字(前面已经对标签解析了,可以取到标签的名字)
判断标签的 localName 是不是对应名字
注意,这判断是交给 delegate 去做的,也就是 BeanDefinitionParserDelegate
从这里就可以看到 delegate 做了两件事
前面提到的,判断是否是默认的命名空间
判断标签是哪种类型
解析 Bean 标签
从上面解析过程可以看到,对于默认的命名空间,支持的标签解析有以下
import 标签
alias 标签
bean 标签
beans 标签
其中对于 bean 标签,解析是最难的,所以把 bean 标签解析看懂了,其他什么的也会很快理解
下面直接看 bean 解析的代码
processBeanDefinition 方法
这个方法就是解析 Bean 标签的
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//获取 BeanDefinitionHolder 对象
//这个对象其实已经包含了标签里面的各种属性,id、class、name、alias 那些
//也就是说,第一步是让 BeanDefinitionDelegate 去解析 Bean 标签,并且获取里面的那些属性
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
//如果获取的属性,进行下面处理
if (bdHolder != null) {
//上面已经将节点解析完了,但是子标签还没进行解析
//所以下面的那一步就是解析子标签,同样去获取 id、name 的那些属性(bean 中的 bean)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 对标签、子标签都解析完了之后
// 将解析的 BeanDefinitionHolder 交给 BeanDefinitionReaderUtils 去进行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 最后发出响应时间,通知相关的监听器,代表这个 bean 已经加载完成了
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
//获取不到 BeanDefinitionHolder,什么都不做
}
其实主要就三个步骤
BeanDefinitionDelegate 解析节点,包括子节点,解析出 BeanDefinitionHolder 对象
BeanDefinitionReaderUtils,使用解析出来的 BeanDefinitionHolder 来进行注册 Bean
注册完后,开始通知相关的监听器,代表这个 Bean 已经加载完了
解析 BeanDefinition
BeanDefinition 其实就是代表一个 Bean(下面是官方文档的说明)
解析 BeanDefinition 是由 BeanDefinitionDelegate 负责的,具体的方法是 parseBeanDefinitionElement
看源码前,先看下官方文档对于 bean 的命名定义
主要源码如下
/**
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) {
// 获取当前 bean 标签的 id 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取当前 bean 标签的 name 属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 对别名进行处理,其实这里并不是指 alias 标签,因为 bean 没有 alias 属性
// 但是一个 bean 可以有多个名字,用逗号或者分号或空格间隔!
// 但 id 只能有一个
// 所以,下面对 name 的操作主要还是针对 aliases
//并且存储在 arrayList 中,arrayList 已经够用了!不需要考虑扩容
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//下面还是继续对 id 和 name 属性的处理
String beanName = id;
//先判断有没有 ID!
//id 是必须要有的
//如果没有 ID 且有 name
//那么第一个 name
就会设置为 ID
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
//进入到这个代码块就代表没有 ID,但有 name
//让第一个 name 作为 ID 属性
//同时,由于第一个 name 作为了 ID 属性,它也不在是 name 属性了
//会从 alias 数组里面移除
beanName = aliases.remove(0);
//来点日志提示没有 ID 属性,并且让第一个 name 为 ID
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//如果提供要解析的 Bean 为 null
//这里可以理解,如果提供解析的 bean 不为 null
//就根据提供解析的 bean 来解析
//如果为 null,就主要就是根据 element 去解析
if (containingBean == null) {
//检查 bean 名称是否正确
//具体来说是验证指定的 bean 名称是否未使用
checkNameUniqueness(beanName, aliases, ele);
}
//对其他标签的解析
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//如果此时还没有 beanName,使用默认规则为该 bean 去生成 beanName
if (!StringUtils.hasText(beanName)) {
try {
//如果有提供解析的 bean
if (containingBean != null) {
//使用 generateBeanName 去生成
//注意这里不会提供 name 属性
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
//如果根据 element 去注册 bean,那代表就是根节点!
//根节点一定有 id 和 name 属性
//下面会对根节点去生成 id 和 name 属性
else {
//否则,用 readContext 去生成 id
beanName = this.readerContext.generateBeanName(beanDefinition);
// 并且还会默认 bean 的 class 名称作为 name 属性
//因为 beanName 还为空的话,肯定是 name 一个都没有提供的
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
//将以 class 生成的 name 保存在 aliases 中
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;
}
}
//将 List 转化为数组
String[] aliasesArray = StringUtils.toStringArray(aliases);
//将解析出来的信息又封装在 BeanDefinitionHolder 中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//如果对没有其他标签
//直接返回 null,不解析了
return null;
}
所以,我们可以将 bean 的解析过程分为下面几步
处理 id 和 name 属性,进行解析和提取
进一步去解析 bean 中的其他属性,并且统一封装在 AbstractBeanDefinition 中,这里还不知道具体在哪个类
此时 bean 已经大体处理完了,不过此时 bean 可能没有 id,所以要保证 id,所以要提供机制去生成
假如解析的 bean 是前面提供的,那么 BeanDefinitionReaderUtils.generateBeanName 方法生成
假如解析的 bean 不是前面提供的,具体根据 element 去生成,那就由 readerContext 去生成
并且还会根据 class 去生成 name!
最后,将获取到的 bean 信息封装到新的 BeanDefinitionHolder 对象!
在这里,我们又认识了一个对象,当 bean 解析完后会生成 beanDefinition 对象,但是里面没有 id 和 name 属性,这些都是单独处理的,所以其实一个完整的 bean,是一个 beanDefinitionHolder 对象
下面就来看看 spring 是怎样做进一步解析 bean 的其他标签的!
/**
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));
//获取 class 属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//获取 parent 属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
评论