spring动态数据源切换

176 阅读1分钟

AbstractRoutingDataSource

1、原理

  • 可在程序中动态设置数据源
  • 给子类提供获取当前数据源名称实现,保证一个线程一个连接
  • 对外提供getConnection方法,选取对应的数据源获取连接

2、使用

  • 1.创建DataSourceContextHolder类,内置ThreadLocal,提供设置datasourceName和移除的方法

  • 2.继承AbstractRoutingDataSource类,实现determineCurrentLookupKey方法,调用DataSourceContextHolder方法

  • 3.所需切换数据源处,注入AbstractRoutingDataSource实现类,调用此三个方法重设数据源
    this.dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
    this.dynamicDataSource.setTargetDataSources(targetDataSources);
    this.dynamicDataSource.afterPropertiesSet();

  • 4.重设数据源后,DataSourceContextHolder中设置新的datasourceName

  • 5.后续此线程获取数据库连接即为刚才设置的。

3、源码

	// 根据当前设置的数据源,返回相应的连接
	@Override
	public Connection getConnection() throws SQLException {
		return determineTargetDataSource().getConnection();
	}

        // 由继承类实现,可定义DataSourceContextHolder类,
	// 使用ThreadLocal保存唯一的数据源名称,使得每个请求线程保有自己本次的数据源
	@Nullable
	protected abstract Object determineCurrentLookupKey();

	// 根据实现的determineCurrentLookupKey方法,动态从resolvedDataSources获取当前数据源
	protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

	// 设置当前数据源
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		this.targetDataSources = targetDataSources;
	}

	// 设置默认数据源即主数据源
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		this.defaultTargetDataSource = defaultTargetDataSource;
	}

	// 将设置的数据源放入resolvedDataSources、resolvedDefaultDataSource
	@Override
	public void afterPropertiesSet() {
		if (this.targetDataSources == null) {
			throw new IllegalArgumentException("Property 'targetDataSources' is required");
		}
		this.resolvedDataSources = new HashMap<>(this.targetDataSources.size());
		this.targetDataSources.forEach((key, value) -> {
			Object lookupKey = resolveSpecifiedLookupKey(key);
			DataSource dataSource = resolveSpecifiedDataSource(value);
			this.resolvedDataSources.put(lookupKey, dataSource);
		});
		if (this.defaultTargetDataSource != null) {
			this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
		}
	}