springboot多数据源使用@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发现并不是这个优先级,也没找到相关的资料,所以之后再研究一下。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,我们可以使用多个数据源,但是默认情况下只能使用一个数据源。如果我们想要使用多个数据源,我们可以使用Dynamic DataSource Routing(动态数据源路由)来解决这个问题。Dynamic DataSource Routing可以根据当前线程的上下文信息来动态地切换数据源。 在使用Dynamic DataSource Routing之前,我们需要使用Spring Boot的AOP功能来拦截MyBatis的Mapper接口,并根据当前线程的上下文信息来动态地切换数据源。具体步骤如下: 1.添加依赖 ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${dynamic.datasource.version}</version> </dependency> ``` 2.配置数据源 ```yaml spring: datasource: master: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver slave1: url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver ``` 3.配置动态数据源 ```java @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave1") public DataSource slave1DataSource() { return DataSourceBuilder.create().build(); } @Bean public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slave1DataSource") DataSource slave1DataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); targetDataSources.put(DataSourceType.SLAVE1.name(), slave1DataSource); return new DynamicDataSource(masterDataSource, targetDataSources); } } ``` 4.配置AOP ```java @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.baomidou.dynamic.datasource.annotation.DS)") public void dsPointCut() { } @Before("dsPointCut()") public void beforeSwitchDS(JoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); DS annotation = signature.getMethod().getAnnotation(DS.class); if (annotation == null) { DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER); } else { DynamicDataSourceContextHolder.setDataSourceType(annotation.value()); } } @After("dsPointCut()") public void afterSwitchDS(JoinPoint point) { DynamicDataSourceContextHolder.clearDataSourceType(); } } ``` 5.使用@DS注解来动态指定数据源 ```java @DS("slave1") public interface UserMapper { List<User> findAll(); } ``` 以上就是在Spring Boot中配置多数据源的方法,通过Dynamic DataSource Routing来实现动态切换数据源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值