Spring Bean 注解注入命名源码分析

欢迎光临我的GitHubPages 无涯

前言

上篇文章讲到了XML配置方式在不指定ID的情况下,Spring的BeanName。今天来谈论下,现代Spring中Bean注册使用最多的方式—注解。注解方式生成的BeanName策略又是怎样的呢。

Spring 常用注册Bean注解

  • @Component@Service@Repository@Controller 这四个注解用于类上,实质上是一样的,能够注册当前类到容器,value属性就是BeanName
  • @Configuration这个注解同样用作类上,不同的是,这个注解通常与@Bean配合使用,注册方法的返回类型对象,用作配置。
  • @Bean 用于方法上,该方法需要在@Configuration标注的类里面,且方法必须为public

AnnotationBeanNameGenerator

@Component@Service@Repository@Controller@Configuration

这五个注解注册的Bean名称都由AnnotationBeanNameGenerator生成

public class AnnotationBeanNameGenerator implements BeanNameGenerator {

 /**
  * A convenient constant for a default {@code AnnotationBeanNameGenerator} instance,
  * as used for component scanning purposes.
  * @since 5.2
  */
 public static final AnnotationBeanNameGenerator INSTANCE = new AnnotationBeanNameGenerator();

 private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";

 @Override
 public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  if (definition instanceof AnnotatedBeanDefinition) {
   String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
   if (StringUtils.hasText(beanName)) {
    // Explicit bean name found.
    return beanName;
   }
  }
  // Fallback: generate a unique default bean name.
  return buildDefaultBeanName(definition, registry);
 }
}

该生成器同样实现了父类BeanNameGeneratorgenerateBeanName方法,与默认实现不同的是没有委托给其它类实现功能,而在自身实现,同样存在一个单列对象。

可以看到命名逻辑先是从注解的元信息获取配置的BeanName,在获取不到的情况下回去调用buildDefaultBeanName生成一个名称。

下面我们看下生成逻辑


 /**
  * Derive a default bean name from the given bean definition.
  * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.
  * @param definition the bean definition to build a bean name for
  * @param registry the registry that the given bean definition is being registered with
  * @return the default bean name (never {@code null})
  */
 protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  return buildDefaultBeanName(definition);
 }

 /**
  * Derive a default bean name from the given bean definition.
  * <p>The default implementation simply builds a decapitalized version
  * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
  * <p>Note that inner classes will thus have names of the form
  * "outerClassName.InnerClassName", which because of the period in the
  * name may be an issue if you are autowiring by name.
  * @param definition the bean definition to build a bean name for
  * @return the default bean name (never {@code null})
  */
 protected String buildDefaultBeanName(BeanDefinition definition) {
  String beanClassName = definition.getBeanClassName();
  Assert.state(beanClassName != null, "No bean class name set");
  String shortClassName = ClassUtils.getShortName(beanClassName);
  return Introspector.decapitalize(shortClassName);
 }

该方法委托给另外一个重载方法实现,先获取该Bean的ClassName,然后获取短的命称,即类名,最后decapitalize把类名首字母变为小写。

使用采用这种方式生成的BeanName就是类名首字母小写。例如com.oneyoung.User -> user

@Bean 名称生成策略

使用@Bean方式注册到容器的命名方式与上面有所不同。

加载实现类是ConfigurationClassBeanDefinitionReader专门用来处理配置类的Bean

  AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
  Assert.state(bean != null, "No @Bean annotation attributes");

  // Consider name and any aliases
  List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
  String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

这部分代码可以看出,先去注解的value配置,如果为空则取方法名。所以默认的beanName就是方法名。

上一篇:C++ activemq CMS 学习笔记


下一篇:Linux系统小技巧(5):如何列出和排序正在读写的进程?