【源码】Spring-BeanNameGenerator
BeanNameGenerator 接口
在 Spring 中每个 bean 都要有一个id
或者name
来表示唯一的 bean,在 xml 中定义一个 bean 可以指定其 id 和 name 值,但那些没有指定的,或者注解方式的 beanName 怎么来的呢?这就是 BeanNameGenerator接口的实现类来完成的。
org.springframework.beans.factory.support.BeanNameGenerator
接口是生成 bean 名字的顶级接口。源码如下:
该接口中只有一个方法,方法入参:
BeanDefinition
需要生成 beanName 的 BeanDefinition 对象BeanDefinitionRegistry
注册器,定义了一些对 BeanDefinition 的常用操作 GOTO
该接口有两个主要实现类:
- DefaultBeanNameGenerator 默认的 bean 名称生成实现
- AnnotationBeanNameGenerator 注解方式定义的 bean 名称生成实现
DefaultBeanNameGenerator 实现
DefaultBeanNameGenerator 源码如下:
重写了 generateBeanName() 方法,通过BeanDefinitionReaderUtils
工具类,生成 bean 的名字。
AnnotationBeanNameGenerator 实现
通过类名可以知道,这是给注解方式的 bean 生成 beanName 的。
AnnotationBeanNameGenerator
能够处理 @Component / @Respository / @Service / @Controller 注解,解析为 bean Name 设置给注解对应的 value 属性。
如果没有 value 属性,则根据类的名称生成一个 shortBeanName. eg:it.com.UserServiceImpl -> userServiceImpl
AnnotationBeanNameGenerator 类重写了 generateBeanName() 方法如下:
1.生成注解模式的 beanName
首先判断 入参 BeanDefinition 是否是AnnotatedBeanDefinition
类型的。
如果是,则进入 determineBeanNameFromAnnotation()
方法,如下:
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 并不知道,所以直接抛出异常。
比如,如下的类定义:
就会抛出异常:
(1)来看一下 isStereotypeWithNameValue() 方法的逻辑:
方法上的入参:
- type 当前遍历的 注解
- amd.getMetaAnnotationTypes(type) 当前注解的所有子孙注解(包含自己,除过 Java 自带的元注解),是一个 Set<String>
- attributes 当前遍历的注解的 所有属性
进入方法如下:
首先判断了当前注解是否是 @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()
方法:
首先获取了 类的全类名 className,然后通过ClassUtils.getShortName
获取了短的 className,然后通过Introspector.decapitalize
获取了 beanName,主要是做了首字母变小写的操作。
这里还有一个判断,如果首字母已经是小写,第二个字母已经是大写了,即 aA 格式了,就不做处理,直接返回。
❗❗❗ 注意
这里在生成默认的 beanName 的时候,并没有做唯一性的判断,所以会出现这种场景,在不同包下存在相同名称的类时,都没有设置 beanName,使用默认的生成方式,此时会产生两个 beanName 一样的 bean,这肯定是不允许的,因为 beanName 是需要唯一的,Spring 会抛出异常。
什么时候校验 beanName 唯一性的?
ConfigurationClassPostProcessor 的 processConfigBeanDefinitions() 方法中。
解决方式:
转载自:https://juejin.cn/post/7149451276206473230