写点什么

Spring(四):bean 标签解析

  • 2021 年 11 月 11 日
  • 本文字数:3828 字

    阅读完需:约 13 分钟

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


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


就会设置为 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 对象


进一步解析 bean 中的其他属性


下面就来看看 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);


}

评论

发布
暂无评论
Spring(四):bean标签解析