Spring动态修改@Qualifier注解依赖的beanName

背景

@Qualifier注解的使用场景主要是在Spring容器中存在多个相同的RequiredType时,通过beanName的方式指定依赖的是哪个一个bean实例。

之所以有动态修改@Qualifier注解的value值,是因为我们把多个微服务应用通过Maven依赖的方式整合成了一个单体应用,但是不同的服务中定义了相同的类名导致Spring启动时提示beanName重复。

通过实现AnnotationBeanNameGenerator的方式自定义了beanName的生成方式,取代了Spring默认的类名作为beanName的方式,这个具体过程不表,搜索引擎能搜到很多具体的实现代码。

这样引发了另外一个问题,有些开发同学在定义依赖bean的时候使用@Qualifier注释指定了beanName,因为修改了beanName默认生成方式导致无法找到指定的bean,启动又报错了。

解决方案有两种:

  1. 方案一:适合在开发阶段时使用,对代码有一定的侵入性,有条件还是比较推荐这种实现方式的;
  2. 方案二:通过重载Spring的DefaultListableBeanFactory类,实现无代码入侵的方式动态修改@Qualifier注解指定的beanName。

方案一

使用@Resource注解替换@Qualifier注解,需要注入的beanName可以通过配置项的方式动态指定,实现代码如下:

public class DemoService {
 
    // 通过配置beanName参数指定需要注入的bean实例
    @Resource(name="${beanName}")
    IService iService;
    
    // todo
}

方案二

看了下@Qualifier注解核心实现逻辑(SpringBoot2.0版本)在org.springframework.beans.factory.support.DefaultListableBeanFactory类的

protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor)
方法中,具体干了如下几件事:

  1. 通过requiredType找到所有匹配的bean;
  2. 通过@Qualifier注解的value找到对应的bean并放入result集合返回;

动态修改的思路:

  1. 拿到所有匹配的beanName数组后,加一个自定义的重写@Qualifier注解value值的方法;
  2. 判断当前处理的类是否是业务层代码,需要过滤掉Spring底层和中间件的类,这个可以按照自己的技术架构特点进行判断;
  3. 获取@Qualifier注解原始value值,并根据自己的beanName规则生成新的beanName;
  4. 判断原始的beanName不在匹配的beanName数组中,并且根据自己的beanName规则生成新的beanName在匹配的beanName数组中,说明该@Qualifier注解的value值需要进行重写;
  5. 通过反射的方式动态修改@Qualifier注解的value值,这里大家可以搜索下Java注解实现原理,是通过AnnotationInvocationHandler生成的一个代理类,所有的注解属性存储在AnnotationInvocationHandler类的memberValues属性Map集合中。

实现代码如下:

protected Map<String, Object> findAutowireCandidates(
            @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        
        // 自定义的动态修改@Qualifier注解值
        rewriteQualifierValue(candidateNames, requiredType, descriptor);

        // .......

        return result;
    }
private void rewriteQualifierValue(String[] candidateNames, Class<?> requiredType, DependencyDescriptor descriptor) {
    // 尝试获取@Qualifier注解
    Qualifier qualifier = descriptor.getAnnotation(Qualifier.class);
    // 尝试获取依赖类子产品代码
    String serviceName = PackageUtils.getServiceName(requiredType.getName());
    // 必要性判断
    if (Objects.nonNull(qualifier) && StringUtils.hasText(qualifier.value()) && StringUtils.hasText(serviceName)) {
        // 通过子产品代码生成有依赖对象beanName
        // {@link com.xxx.dgdp.config.PackageBeanNameGenerator}
        String dependencyWithService = new StringBuilder(serviceName).append(StringUtils.capitalize(qualifier.value())).toString();
        // ICO容器中没有@Qualifier中定义的依赖对象
        // ICO容器中存在代子产品的依赖对象
        if (!ArrayUtils.contains(candidateNames, qualifier.value())
                && ArrayUtils.contains(candidateNames, dependencyWithService)) {
            // 获取代理实例
            InvocationHandler annotationInvocationHandler = Proxy.getInvocationHandler(qualifier);
            try {
                // 获取AnnotationInvocationHandler的memberValues属性
                Field memberValues = annotationInvocationHandler.getClass().getDeclaredField("memberValues");
                memberValues.setAccessible(true);
                // 修改value值
                Map<String, Object> values = (Map<String, Object>) memberValues.get(annotationInvocationHandler);
                values.put("value", dependencyWithService);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                logger.error("重写@Qualifier指定依赖beanName时,未找到注解AnnotationInvocationHandler对象memberValues属性");
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值