本文主要阐述用dynamic-datasource-spring-boot-starter配置多数据源时,按需动态初始化数据库or按需加载数据源的问题处理。

        背景:有个做数据处理的组件,数据来源是某几个数据库,这时候可用dynamic-datasource-spring-boot-starter做多数据源配置(具体配置不细述,可见官方文档:https://github.com/baomidou/dynamic-datasource-spring-boot-starter)使用。

场景一:

当数据来源的几个数据库(DB),并不是每个都是必需的情况;比如存在源数据库A、B、C,只需要其中一个库能提供数据即可,但是我们不知道具体哪个数据库是可达的。

这种情况下,你会发现一个问题:当你所配置的多个数据源里面,只要有1个数据源无法访问,整个程序在启动时就会报错终止,不再运行。

那么怎么处理,能让当某个数据库连不上时,只影响该数据源本身,而不是终止程序?

最简单的方式是懒启动(3.3.2版本后支持),配置:spring.datasource.dynamic.lazy=true后,只有需要用到连接的时候才初始化连接池。当然有一定影响:如果使用了有问题的源会一直抛异常。不过也能满足这种场景。

场景二:

当数据来源不仅仅是DB,可能是KAFKA等消息组件、接口获取、FTP等等(DB只是其中一种方式)。

这种情况下,你会发现另一个问题:当不从DB获取数据时,可能你的运行环境并没有DB,无法配置数据源,但是由于引用了dynamic-datasource-spring-boot-starter,系统启动报错。

参照场景一,该情况配置懒启动是可以将程序跑起来的,但是后台会一直抛异常,影响调试,强迫症也受不了~

我们想实现的效果是:用某环境变量来标识数据源类型(比如source.type=DB/KAFKA/FTP/HTTP 来标识),当source.type=DB时,则启动项目正常加载db连接池,加载datasource;当source.type不为DB时,则启动项目不加载datasource相关内容,程序不报错不抛异常;

接下来是解决问题的过程

1、baomidou/dynamic-datasource-spring-boot-starter 文档并没有提供相应的开关功能;于是我扒了下源码,发现文档虽然没提供,但是相关的配置类还是有开关的,如下图:

这个环境变量并没有暴露出来,尝试将spring.datasource.dynamic.enabled配置为false,这时启动项目确实不加载我们配置的dynamic-datasource多数据源了,但是依然报错无法启动:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

由于我们引用了jdbc相关包,springboot启动时会自动装配,在配置文件无法找到相关的数据库连接配置信息,就会抛出该异常。在我们这种场景下,解决方式也很简单,在springboot应用程序启动时,排除jdbc的自动装配机制即可。有两种方式:

一:在启动类相关注解加入exclude = DataSourceAutoConfiguration.class

 

二:在配置文件中配置: 

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

修改完后启动项目,解决了该问题,但是仍然无法启动,抛出另一个错误:Error creating bean with name 'XXXMapper' defined in file [D:\work\projects\***\XXXMapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

通俗点讲,这是由于springboot程序启动后,并没有加载相关db连接,但是某些类创建bean时用到db相关的东西,导致错误。

解决办法也简单,有两点:

1、在相关类上也用source.type开关控制是否加载,可用@ConditionalOnProperty注解控制:

 

2、容易忽略的一点,启动类上的@MapperScan 也需用条件控制,否则springboot程序启动时,也会去创建XXXMapping类的bean导致失败。简单点的方法是新建一个空的config类,将@MapperScan注解迁移到新建的类上,然后配合@Configuration 和 @ConditionalOnProperty来曲线救国:

 这时候,程序就能正常启动了,根据soure.type来加载不同的数据源;

优化项:

这种情况下,程序有两个配置项需要绑定:source.type 和 spring.datasource.dynamic.enabled

当source.type=DB,则spring.datasource.dynamic.enabled=true

反之,当source.type不为DB,则spring.datasource.dynamic.enabled=false

是不是感觉有点麻烦~ 怎么简化下,只用source.type来控制呢?

我采用的方式是:

1、配置文件里常驻 spring.datasource.dynamic.enabled=false,这时DynamicDataSourceAutoConfiguration.class不生效;

2、重写baomidou/dynamic-datasource-spring-boot-starter 的DynamicDataSourceAutoConfiguration.class 类,将启用条件换成source.type

 有没有发现,之前为@MapperScan新建的空配置类,也可以省略了,直接将@MapperScan注解放在这个类上~

至此,整个问题得到解决;

当然肯定有更好的方法~持续探索吧~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐