【源码分析】Spring容器中Bean的命名规则

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构建BeanName(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的名称呢?

思路:

  1. 有源码分析可知,BeanNameGenerator类是Bean名称的策略接口,因此需要自定义类来实现这个接口。
  2. 同时需要实现接口的generateBeanName方法,通过generateBeanName方法来指定Bean的命名规则。
  3. 之后可以通过@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);
    }
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值