无涯

无所谓无 无所谓有

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

前言

上篇文章讲到了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生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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生成一个名称。

下面我们看下生成逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

/**
* 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

1
2
3
4
5
6
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就是方法名。