likes
comments
collection
share

【源码】Spring-BeanNameGenerator

作者站长头像
站长
· 阅读数 13

BeanNameGenerator 接口

在 Spring 中每个 bean 都要有一个id或者name来表示唯一的 bean,在 xml 中定义一个 bean 可以指定其 id 和 name 值,但那些没有指定的,或者注解方式的 beanName 怎么来的呢?这就是 BeanNameGenerator接口的实现类来完成的。

org.springframework.beans.factory.support.BeanNameGenerator 接口是生成 bean 名字的顶级接口。源码如下:

【源码】Spring-BeanNameGenerator

该接口中只有一个方法,方法入参:

  • BeanDefinition 需要生成 beanName 的 BeanDefinition 对象
  • BeanDefinitionRegistry 注册器,定义了一些对 BeanDefinition 的常用操作 GOTO

该接口有两个主要实现类:

  • DefaultBeanNameGenerator 默认的 bean 名称生成实现
  • AnnotationBeanNameGenerator 注解方式定义的 bean 名称生成实现

DefaultBeanNameGenerator 实现

DefaultBeanNameGenerator 源码如下:

【源码】Spring-BeanNameGenerator

重写了 generateBeanName() 方法,通过BeanDefinitionReaderUtils工具类,生成 bean 的名字。

AnnotationBeanNameGenerator 实现

通过类名可以知道,这是给注解方式的 bean 生成 beanName 的。

AnnotationBeanNameGenerator能够处理 @Component / @Respository / @Service / @Controller 注解,解析为 bean Name 设置给注解对应的 value 属性。

如果没有 value 属性,则根据类的名称生成一个 shortBeanName. eg:it.com.UserServiceImpl -> userServiceImpl

AnnotationBeanNameGenerator 类重写了 generateBeanName() 方法如下:

【源码】Spring-BeanNameGenerator

1.生成注解模式的 beanName

首先判断 入参 BeanDefinition 是否是AnnotatedBeanDefinition类型的。

如果是,则进入 determineBeanNameFromAnnotation()方法,如下:

【源码】Spring-BeanNameGenerator

1.获取注解类的元信息,获取到的结果被封装为 AnnotationMetadata 对象。

2.从注解元信息 AnnotationMetadata 上,获取类上所有注解的类型。

一个类上可能存在多个注解,所以返回 set,也意味着同一个注解只能使用一个。

3.遍历所有注解。

4.获取该注解的所有属性。

通过 AnnotationConfigUtils 的 attributesFor()方法获取,获取到的结果为 AnnotationAttributes 对象。

5.该注解有属性,并且 isStereotypeWithNameValue()方法返回 true 时。

获取 value 属性,value 属性为字符串类型,并且有值时,继续下一步。

如果此时 beanName 已经有值,且 beanName 和 value 属性值不相同时,抛出 IllegalStateException 异常。

如果此时 beanName 无值时,则将 value 属性作为 beanName。

该类上的所有注解都循环完毕后,才返回 beanName。

❓ 什么场景下会抛出异常?

当同一个类上,同时出现多个 @Component 相关的注解,然后分别定义了不同的 beanName,那么使用哪一个呢?Spring 并不知道,所以直接抛出异常。

比如,如下的类定义:

【源码】Spring-BeanNameGenerator

就会抛出异常:

【源码】Spring-BeanNameGenerator

(1)来看一下 isStereotypeWithNameValue() 方法的逻辑:

方法上的入参:

-   type 当前遍历的 注解
-   amd.getMetaAnnotationTypes(type) 当前注解的所有子孙注解(包含自己,除过 Java 自带的元注解),是一个 Set<String>
-   attributes 当前遍历的注解的 所有属性

进入方法如下:

【源码】Spring-BeanNameGenerator

首先判断了当前注解是否是 @Component 类型的,或者 子孙注解中有 @Component 类型的,或者是javax.annotation.ManagedBean 类型的,或者是 javax.inject.Named 类型的。

如果是这些符合要求的注解,并且注解存在属性,并在存在 value 属性。则返回 true。

(2) 来看一下 amd.getMetaAnnotationTypes(type) 方法

详见:GOTO

2.否则生成默认的 beanName

如果入参 BeanDefinition 不是AnnotatedBeanDefinition类型的,或者虽然是该类型的,但是没有获取到 beanName 时,生成默认的 beanName.

比如,如下的这种写法,就需要生成默认的 beanName:

@Service
public class HxVmsMediaLineService{

}

进入buildDefaultBeanName()方法:

【源码】Spring-BeanNameGenerator

首先获取了 类的全类名 className,然后通过ClassUtils.getShortName获取了短的 className,然后通过Introspector.decapitalize获取了 beanName,主要是做了首字母变小写的操作。

【源码】Spring-BeanNameGenerator

这里还有一个判断,如果首字母已经是小写,第二个字母已经是大写了,即 aA 格式了,就不做处理,直接返回。

❗❗❗ 注意

这里在生成默认的 beanName 的时候,并没有做唯一性的判断,所以会出现这种场景,在不同包下存在相同名称的类时,都没有设置 beanName,使用默认的生成方式,此时会产生两个 beanName 一样的 bean,这肯定是不允许的,因为 beanName 是需要唯一的,Spring 会抛出异常。

什么时候校验 beanName 唯一性的?

ConfigurationClassPostProcessor 的 processConfigBeanDefinitions() 方法中。

解决方式:

www.cnblogs.com/zhusen/p/12…

blog.csdn.net/liu_xue_xue…

转载自:https://juejin.cn/post/7149451276206473230
评论
请登录