欢迎光临我的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);
}
}
该生成器同样实现了父类BeanNameGenerator
的generateBeanName
方法,与默认实现不同的是没有委托给其它类实现功能,而在自身实现,同样存在一个单列对象。
可以看到命名逻辑先是从注解的元信息获取配置的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
就是方法名。