对多数据源大家应该不陌生,一般的在单个应用都会存在一个数据库,一个文件存储。这里所说的数据库就是我们描述的数据源。那么多数据源的意思其实通俗来讲就是在一个单体应用中存在两个以上的数据库。这个时候就需要我们对多个数据源进行分别对待进行处理了。
理解多数据源的概念
在之前的文章中我们了解了在Spring Boot如何整合单个数据源,并且我们也提到了数据源整合时候的原理。下面我们就依托于之前提到的原理来构建我们的多数据源应用。
之前文章结尾的时候我们提到了在整合Druid数据库连接池的时候,在MyBatis自动注入的注解上有如下一个配置。
@ConditionalOnSingleCandidate(DataSource.class)
而对于@ConditionalOnSingleCandidate注解,我们在讲条件注解的时候曾经提及到,它的意思就是在容器中只有一个DataSource实例的时候,这个自动配置类才会被加载生效。这就与我们上面提到的概念相互冲突了。为什么这么说呢?
因为根据这个注解的意思,容器中只能存在一个数据源,如果需要多个数据源那就不能使用MyBatis框架来进行整合了。那岂不是什么事情都干不了呢?根据字面的意思是可以这样理解的,但是实际上并不是这样。
多数据源的意思其实并不是在同一时刻有多个数据源同时被操作。在Spring框架中,提供了一个AbstractRoutingDataSource的类,根据字面意思,是用来进行数据源路由的,路由的意思就是相互之间可以切换。也就是说可以实现数据源之间的动态切换操作。所以被称为是动态数据源操作。所以这里MyBatis框架所支持其实是一个动态数据源的切换而不是想我们所理解的在同一时刻多个数据源共同操作的场景。
既然理解到这里了?那么我们就来研究一下在Spring Boot中如何去实现动态数据源。
动态数据源
根据上面的意思,动态数据源首先能做的事情就是可以实现自由路由操作。也就是说可以随时切换到其中的一个数据源上。在Spring框架中提供了AbstractRoutingDateSource这样一个类。其源码如下
public abstract class AbstractRoutingDataSource
extends AbstractDataSource implements InitializingBean {
@Nullable
private Map<Object, Object> targetDataSources;
会看到在这个类中有如下一个属性。
private Map<Object, Object> targetDataSources;
这个属性是一个Map结构,也就是说可以用KV键值对来表示。其中K表示需要切换的规则,而V则表示所要切换的数据源。
自习研究这个类我们会发现在这个抽象类中大部分的方法都被实现了,但是唯独在结束的时候有如下这样一个方法没有被实现。需要等待其继承类来实现
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
@Nullable
protected abstract Object determineCurrentLookupKey();
根据上面的英文描述结合自己的理解,我们可以知道,这个的返回值就决定了我们需要切换的数据源所选择的Key是什么。也就是说我们通过这里获取到Key从targetDataSources中获取到对应的数据源的值。
到这里我们就很容易理解数据源的切换逻辑,并且很好的理解动态数据源在SpringBoot中是如何实现的了?那么既然数据源是属于一个共享资源,那么我们就必须要去考虑到多线程线程安全的问题?在Spring Boot中是如何保证数据源线程安全的呢