BeanNameGenerator

我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指定其id和name值,但那些没有指定的,或者注解的spring的beanname怎么来的的?就是BeanNameGenerator接口实现的特性。

  1. <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  2. <property name="entityManagerFactory" ref="entityManagerFactory" />
  3. </bean>

BeanNameGenerator 接口位于 org.springframework.beans.factory.support 包下面,只声明了一个方法,接受两个参数:
image.png

  • definition被生成名字的BeanDefinition实例;
  • registry生成名字后注册进的BeanDefinitionRegistry

BeanNameGenerator有两个实现版本,DefaultBeanNameGeneratorAnnotationBeanNameGenerator。其中:

  • DefaultBeanNameGenerator是给资源文件加载bean时使用(BeanDefinitionReader中使用);
  • AnnotationBeanNameGenerator是为了处理注解生成bean ``name的情况。


DefaultBeanNameGenerator

DefaultBeanNameGenerator类将具体的处理方式委托给了,BeanDefinitionReaderUtils.generateBeanName(BeanDefinition, BeanDefinitionRegistry)方法处理。
image.png
这个方法也没有做任何处理,直接委托了给了BeanDefinitionReaderUtils.``generateBeanName(BeanDefinition , BeanDefinitionRegistry , boolean )方法,多指定了一个boolean型参数,是为了区分内部bean(innerBean)和顶级bean(top-level bean).
image.png
isInnerBean = false:表示这是一个内部bean
image.png

  1. public static String generateBeanName(
  2. BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
  3. throws BeanDefinitionStoreException {
  4. //generatedBeanName定义为类前缀, 读取bean的className,不一定是运行时的实际类型。
  5. String generatedBeanName = definition.getBeanClassName();
  6. if (generatedBeanName == null) {
  7. //如果类名称为空,那读取bean的parent bean name
  8. if (definition.getParentName() != null) {
  9. generatedBeanName = definition.getParentName() + "$child";
  10. }
  11. //否则,读取生成该bean的factoryBean name名称做前缀。
  12. else if (definition.getFactoryBeanName() != null) {
  13. generatedBeanName = definition.getFactoryBeanName() + "$created";
  14. }
  15. }
  16. //generatedBeanName为空字符串,抛出异常
  17. if (!StringUtils.hasText(generatedBeanName)) {
  18. throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
  19. "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
  20. }
  21. String id = generatedBeanName;
  22. //当为内部bean时,调用系统底层的object唯一标识码生成
  23. if (isInnerBean) {
  24. // Inner bean: generate identity hashcode suffix.
  25. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
  26. }
  27. //否则即为顶级bean,生成策略是前缀+循环数字,直到找到对应自增id
  28. else {
  29. // Top-level bean: use plain class name.
  30. // Increase counter until the id is unique.
  31. int counter = -1;
  32. while (counter == -1 || registry.containsBeanDefinition(id)) {
  33. counter++;
  34. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
  35. }
  36. }
  37. return id;
  38. }

整理下流程:生成流程分为前后两部分,前面生成的叫前缀,后面生成的叫后缀。

  1. 读取待生成name实例的类名称,未必是运行时的实际类型。
  2. 如果类型为空,则判断是否存在parent bean,如果存在,读取parent bean的name+”$child”。
  3. 如果parent bean 为空,那么判断是否存在factory bean ,如存在,factory bean name + “$created”.
  4. 前缀生成完毕。如果前缀为空,直接抛出异常,没有可以定义这个bean的任何依据。
  5. 前缀存在,判断是否为内部bean(innerBean,此处默认为false),如果是,最终为前缀+分隔符+十六进制的hashcode码
  6. 如果是顶级bean(top-level bean ),则判断前缀+数字的bean是否已存在,循环查询,知道查询到没有使用的id为止。

处理完成。DefaultBeanNameGenerator处理的问题就这些了。

AnnotationBeanNameGenerator

AnnotationBeanNameGenerator能够处理 Component,Respository,Service,Controller这四个常用的注解,解析为bean name注给他们对应的value属性。另外jee的javax.annotation.ManagedBean和javax.inject.Named也可以支持。

当Component,Respository,Service,Controller注解的value属性没有自定义时,会根据类的名称生成一个短的bean name。例如: com.xyz.FooServiceImpl -> fooServiceImpl
image.png

  1. public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  2. //判断是否是否是AnnotatedBeanDefinition的子类, AnnotatedBeanDefinition是BeanDefinition的一个子类
  3. //如果是AnnotatedBeanDefinition , 按照注解生成模式生成信息,否则生成默认的bean name
  4. if (definition instanceof AnnotatedBeanDefinition) {
  5. String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
  6. //保证生成的bean name 非空
  7. if (StringUtils.hasText(beanName)) {
  8. // Explicit bean name found.
  9. return beanName;
  10. }
  11. }
  12. // Fallback: generate a unique default bean name.
  13. return buildDefaultBeanName(definition, registry);
  14. }

先从相对简单的default看起,这段代码的疑点是生成的bean name并没有和DefaultBeanNameGenerator一样做唯一性校验,可能导致不同包下面存在相同的类名时,会产生两个name一样的bean,引发spring 异常。
image.png
其实ClassUtils.getShortName也很简单,根据传入字符串获取一个具体类名称,不含包路径,考虑cglib代理的类,做了一个特殊处理。

  1. public static String getShortName(String className) {
  2. Assert.hasLength(className, "Class name must not be empty");
  3. int lastDotIndex = className.lastIndexOf(46);
  4. int nameEndIndex = className.indexOf("$$");
  5. if (nameEndIndex == -1) {
  6. nameEndIndex = className.length();
  7. }
  8. String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
  9. shortName = shortName.replace('$', '.');
  10. return shortName;
  11. }

AnnotationBeanDefinitionbeanName如何生成,具体处理委托给了determineBeanNameFromAnnotation(AnnotatedBeanDefinition)方法完成,该方法对该类的所有注解进行了遍历,满足三个条件:
(1)注解名称在待解析列表;
(2)存在value属性且非空;
(3)当且只有一个注解正确配置value属性。

  1. protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
  2. //获取注解类元信息
  3. AnnotationMetadata amd = annotatedDef.getMetadata();
  4. //一个类存在多个注解,故类型为集合
  5. Set<String> types = amd.getAnnotationTypes();
  6. String beanName = null;
  7. for (String type : types) {
  8. //获取该类型对应的属性
  9. AnnotationAttributes attributes = MetadataUtils.attributesFor(amd, type);
  10. //判断注解类型是否包含value属性
  11. if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
  12. String value = (String) attributes.get("value");
  13. if (StringUtils.hasLength(value)) {
  14. //不多于1个注解配置了value属性且非空,比如无法在一个类上面同时使用Component和Sevice注解同时配置beanName值
  15. if (beanName != null && !value.equals(beanName)) {
  16. throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
  17. "component names: '" + beanName + "' versus '" + value + "'");
  18. }
  19. beanName = value;
  20. }
  21. }
  22. }
  23. return beanName;
  24. }

这个方法里面有两个关键的处理流程,第一步,读取对应annotationType对应的所有属性。

  1. public static AnnotationAttributes attributesFor(AnnotationMetadata metadata, String annoClassName) {
  2. return AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annoClassName, false));
  3. }

第二步,判断annotationType是否具有value属性

  1. protected boolean isStereotypeWithNameValue(String annotationType,Set<String> metaAnnotationTypes, Map<String, Object> attributes) {
  2. boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
  3. (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
  4. annotationType.equals("javax.annotation.ManagedBean") ||
  5. annotationType.equals("javax.inject.Named");
  6. return (isStereotype && attributes != null && attributes.containsKey("value"));
  7. }

整理下思路:
生成bean name有两条处理线,使用AnnotationBeanDefinition注解和不使用的。

  • 不使用AnnotationBeanDefinition注解的,直接将类名(不含包名)改为驼峰形式作为bean name。
  • 使用AnnotationBeanDefinition注解的:

  1,读取所有注解类型
  2,遍历所有注解类型,找到所有为Component、Service,Respository,Controller含有非空value属性的注解
  3,不多于一个个有效配置时生效,大于一个会抛出异常。(spring无法明确具体哪个生效)