Spring容器中Bean的命名规则
1.什么是Spring容器?
Spring容器是Spring框架的核心,Spring容器管理者Spring应用中Bean的生命周期。Spring容器负责实例化、配置和组装Bean对象,同时Spring容器页管理着Bean之间的依赖关系。
那么Spring在将Bean对象放入到容器时,是如何实现对Bean命名?
基于以上问题来说明在Spring容器中Bean的命名规则:
在Spring容器中通过BeanNameGenerator接口来定义Bean的命名策略,在BeanNameGenerator接口中有DefaultBeanNameGenerator和AnnotationBeanNameGenerator两个实现类。DefaultBeanNameGenerator类是BeanNameGenerator接口的默认实现。AnnotationBeanNameGenerator类会规定使用@Component、@Bean、@Repository、@Service、@Controller、@Named等注解的类在注入到Spring容器时Bean的命名规则。
2.基于AnnotationBeanNameGenerator为例介绍BeanName的命名规则
接下来以AnnotationBeanNameGenerator实现类为例,来说明Bean的命名规则:
首先在AnnotationBeanNameGenerator
类中重写了BeanNameGenerator
类中的generateBeanName()
方法用于返回Bean的Name;
/**
* 获取Bean的Name
* @param definition Bean的定义信息
* @param registry Bean定义信息注册器
* @return BeanName
*/
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 1 判断当前Bean的定义信息是否是AnnotatedBeanDefinition类型,如果是则会将其赋值给annotatedBeanDefinition变量
if (definition instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
//determineBeanNameFromAnnotation方法的根本目的就是从注解的属性信息中获取Bean的Name
// 1.1 获取注解属性中Bean的Name
String beanName = determineBeanNameFromAnnotation(annotatedBeanDefinition);
if (StringUtils.hasText(beanName)) {
// 如果从注解的属性信息中获取到Bean名称,则直接返回
return beanName;
}
}
// 2 如果没有从Bean的属性信息中获取到Bean的Name,则会通过buildDefaultBeanName方法来构建Bean的名称
// 2.1 构建Bean的Name
return buildDefaultBeanName构建Bean的Name(definition, registry);
}
// 1.1 获取注解属性信息中Bean的Name
@Nullable
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
// 获取Bean的元信息
AnnotationMetadata amd = annotatedDef.getMetadata();
// getAnnotationTypes方法用于返回当前类上所有注解的全限定类名
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
for (String type : types) {
// 获取当前类上注解和属性的映射值
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
if (attributes != null) {
Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
Set<String> result = amd.getMetaAnnotationTypes(key);
return (result.isEmpty() ? Collections.emptySet() : result);
});
// 如果注解上有value属性且不为”“则将其作为Bean的Name返回
if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
Object value = attributes.get("value");
if (value instanceof String strVal && !strVal.isEmpty()) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
}
return beanName;
}
// 2.1构建Bean的Name
protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 2.1.1
return buildDefaultBeanName(definition);
}
// 2.1.1
protected String buildDefaultBeanName(BeanDefinition definition) {
// 获取Bean的全限定类名
String beanClassName = definition.getBeanClassName();
// 判断Bean的全限定类名是否为null,若为null则抛出异常信息
Assert.state(beanClassName != null, "No bean class name set");
// 通过ClassUtils工具类提供的getShortName方法截取Bean的类名
String shortClassName = ClassUtils.getShortName(beanClassName);
// StringUtils的uncapitalizeAsProperty方法,将类名的首字母小写并返回
return StringUtils.uncapitalizeAsProperty(shortClassName);
}
梳理:
- 从当前类的指定注解中获取Bean的value属性,如果value属性不为
”“
的情况下,会将其作为Bean的Name返回。- 如果,没有从类的注解信息中获取到Bean的Name,则会通过buildDefaultBeanName方法来构建Bean的Name。由源码分析可知,buildDefaultBeanName方法会获取到当前类的类名,并将类名的首字母小写来作为Bean的Name来返回。
那么,有源码分析可知,我们该如何通过自定义的方式来构建Bean的名称呢?
思路:
- 有源码分析可知,BeanNameGenerator类是Bean名称的策略接口,因此需要自定义类来实现这个接口。
- 同时需要实现接口的
generateBeanName
方法,通过generateBeanName
方法来指定Bean的命名规则。- 之后可以通过
@ComponentScan
注解的nameGenerator
属性来指定我们自定类即可实现自定义Bean的命名规则。
3.自定义Bean的Name实例
下面,将通过注解方式加入到Spring容器的Bean对象统一按照”wyb + 类名“的方式注入到Spring容器中:
/**
* Package: com.wyb.component.config.MvcConfig
* Description: 自定义Bean名称注解
*
* @Version 1.0
*/
public class CusAnnoBeanNameGenerator implements BeanNameGenerator {
private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
private final Map<String, Set<String>> metaAnnotationTypesCache = new ConcurrentHashMap<>();
/**
* 定义Bean名称
*
* @param definition Bean的定义信息
* @param registry Bean定义信息注册器
* @return Bean名称
*/
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 判断当前Bean是否是通过注解注入
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) definition;
// 从注解中获取Bean的定义名称
String beanName = determineBeanNameFromAnnotation(beanDefinition);
// 如果从注解属性中获取到的Bean名称不为null,则直接返回
if (StringUtils.hasText(beanName)) {
return beanName;
}
}
// 组件属性为指定Bean名称,则通过默认规则进行创建
return buildDefaultCusAnnoBeanName(definition, registry);
}
/**
* 构建自定义Bean的命名规规则 => 将Bean命名规则修改为cus + Bean的类名称
*
* @param definition Bean定义信息
* @param registry Bean定义信息注册器
* @return 自定义规则的Bean名称
*/
private String buildDefaultCusAnnoBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// 获取Bean的全限定类名
String beanClassName = definition.getBeanClassName();
// 获取Bean的类名
String shortName = ClassUtils.getShortName(beanClassName);
// 返回自定义Bean名称
return "wyb" + shortName;
}
private String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
// 获取Bean的源数据信息
AnnotationMetadata amd = annotatedDef.getMetadata();
// 获取Bean注解类型的字符串列表
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
for (String type : types) {
// 获取注解的参数信息
AnnotationAttributes attributes = AnnotationAttributes.fromMap(amd.getAnnotationAttributes(type));
if (attributes != null) {
Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
Set<String> result = amd.getMetaAnnotationTypes(key);
return (result.isEmpty() ? Collections.emptySet() : result);
});
if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
Object value = attributes.get("value");
if (value instanceof String) {
String strVal = (String) value;
if (!strVal.isEmpty()) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
}
}
return beanName;
}
/**
* @param annotationType 待检查的注解类的名称
* @param metaAnnotationTypes 给定注解上的元注解名称
* @param attributes 给定注解的参数映射
* @return
*/
protected boolean isStereotypeWithNameValue(String annotationType,
Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
annotationType.equals("jakarta.annotation.ManagedBean") ||
annotationType.equals("jakarta.inject.Named");
return (isStereotype && attributes != null && attributes.containsKey("value"));
}
}
@SpringBootApplication
// 通过nameGenerator属性来指定Bean的Name的命名规则
@ComponentScan(nameGenerator = CusAnnoBeanNameGenerator .class)
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}