我的编程空间,编程开发者的网络收藏夹
学习永远不晚

springboot多数据源使用@Qualifier自动注入无效的解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

springboot多数据源使用@Qualifier自动注入无效的解决

@Qualifier自动注入无效的解决

问题

使用springboot进行多数据源时,发生了单例DataSource对应多个DataSourceBean的问题。

具体错误如下:XXXXX required a single bean, but 3 were found。通过@Qualifier来区分,或是在@Bean中添加name属性来区分,都没有作用。

问题的根本原因

主要在于SpringBoot的DataSourceInitializer,该类在autoConfigure包中,用来自动初始化一个内置的DataSource实例,在创建该实例的时候发生了注入的问题。

创建实例的流程(记录一下方便以后再次调试):

1. 调用DataSourceInitializer的构造方法

2. 调用AbstractAutowireCapableBeanFactory的applyMergedBeanDefinitionPostProcessors方法

3. 调用AbstractAutowireCapableBeanFactory的initializeBean方法

4. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInitialization方法,其中有一个循环是用多种Bean处理器来处理DataSourceInitializer对象

5. 之后使用反射的方式跳转到DataSourceInitializer的init方法

6.里面通过this.applicationContext.getBean(DataSource.class)来获取所有DataSource的实现类对象实例。

7. DefaultListableBeanFactory的resolveNamedBean方法中来选取实例对象,通过里面的getBeanNamesForType方法获取到所有的符合requireType(也就是DataSource.class)的对象。

8. 如果对象实例的实例数量大于1,则会进入以下的两个判断:

判断是否有Primary的实例,或者是优先级高的实例对象,如果有,则将候选对象名赋值给candidateName。没有则置为空,最后抛出多个实例的异常。

其中问题出在

因为项目中只使用了@Qualifier,而且springboot的DataSourceInitializer没有对@Qualifier的处理,所以没有对实例进行匹配。

造成多个数据源实例。对于存在多数据源的情况,他们做的补救措施是在代码中添加了是否是Primary和是否是HighestPriority的判断,

来处理采用哪个数据源。所以解决的方式也因此出来了。就是将某个实例标记为Primary或者HighestPriority。

解决问题的方法

在某个@Bean上添加@Primary注解,就可以做到唯一区分。

这里其实应该还可以通过优先级来区分,但使用@Order发现并不是这个优先级,也没找到相关的资料,所以之后再研究一下。

@Qualifier的作用和应用

@Qualifier的作用

这是官方的介绍

This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.

简单的理解就是:

  • 在使用@Autowire自动注入的时候,加上@Qualifier(“test”)可以指定注入哪个对象;
  • 可以作为筛选的限定符,我们在做自定义注解时可以在其定义上增加@Qualifier,用来筛选需要的对象。这个理解看下面的代码吧,不好解释。

功能介绍

首先是对(1)的理解。


//我们定义了两个TestClass对象,分别是testClass1和testClass2
//我们如果在另外一个对象中直接使用@Autowire去注入的话,spring肯定不知道使用哪个对象
//会排除异常 required a single bean, but 2 were found
@Configuration
public class TestConfiguration {
   @Bean("testClass1")
   TestClass testClass1(){
       return new TestClass("TestClass1");
   }
   @Bean("testClass2")
   TestClass testClass2(){
       return new TestClass("TestClass2");
   }
}

下面是正常的引用


@RestController
public class TestController {
 
    //此时这两个注解的连用就类似 @Resource(name="testClass1")
    @Autowired
    @Qualifier("testClass1")
    private TestClass testClass;
 
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }

}

@Autowired和@Qualifier这两个注解的连用在这个位置就类似 @Resource(name=“testClass1”)

对(2)的理解


@Configuration
public class TestConfiguration {
    //我们调整下在testClass1上增加@Qualifier注解
    @Qualifier
    @Bean("testClass1")
    TestClass testClass1(){
        return new TestClass("TestClass1");
    }
 
    @Bean("testClass2")
    TestClass testClass2(){
        return new TestClass("TestClass2");
    }
}
@RestController
public class TestController {
    //我们这里使用一个list去接收testClass的对象
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();
    
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }
}

我们调用得到的结果是

[
{
"name": "TestClass1"
},
{
"name": "TestClass2"
}
]

我们可以看到所有的testclass都获取到了。接下来我们修改下代码


@RestController
public class TestController {
 
    @Qualifier //我们在这增加注解
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();
 
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }
}

和上面代码对比就是在接收参数上增加了@Qualifier注解,这样看是有什么区别,我们调用下,结果如下:

[
{
"name": "TestClass1"
}
]

返回结果只剩下增加了@Qualifier注解的TestClass对象,这样我们就可以理解官方说的标记筛选是什么意思了。

另外,@Qualifier注解是可以指定value的,这样我们可以通过values来分类筛选想要的对象了,这里不列举代码了,感兴趣的同学自己试试。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

springboot多数据源使用@Qualifier自动注入无效的解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

vue中动态渲染数据时使用$refs无效的解决

这篇文章主要介绍了vue中动态渲染数据时使用$refs无效的解决方案,具有很好的参考价值。希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-01-28

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录