有的时候我们的代码同时会用到多个数据库,因此需要在请求mysql前,切换数据源
通过aop+注解的方式能轻松实现多数据源
这里介绍使用spring+aspectj自动代理+注解的方式实现mybatis多数据源的切换
知识点
- 数据源路由器AbstractRoutingDataSource,继承该类的数据源,每次请求时都会通过determineCurrentLookupKey方法决定使用的数据源
- @annotation切入点表达式
多数据源
首先我们需要创建多个链接池(数据源)
/** DynamicDataSourceProperties.java **/
//配置类
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/**
* 数据源配置,这里继承了springboot的单数据源配置类,并且为配置分配了key
*/
@Component
@Primary
@ConfigurationProperties("datasource.dynamic.properties")
public class DynamicDataSourceProperties extends DataSourceProperties {
private String key;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
/** DynamicDataSourceRegisterProperties.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 动态多数据源配置文件
*/
@Component
@ConfigurationProperties("datasource.dynamic.register")
public class DynamicDataSourceRegisterProperties {
public String defaultKey;
public List<DynamicDataSourceProperties> properties;
public List<DynamicDataSourceProperties> getProperties() {
return properties;
}
public void setProperties(List<DynamicDataSourceProperties> properties) {
this.properties = properties;
}
public String getDefaultKey() {
return defaultKey;
}
public void setDefaultKey(String defaultKey) {
this.defaultKey = defaultKey;
}
}
# application.properties
# 多数据源配置
# 第一个数据源
datasource.dynamic.register.properties[0].key=xss
datasource.dynamic.register.properties[0].url=jdbc:mysql://127.0.0.1:3306/xss
datasource.dynamic.register.properties[0].username=root
datasource.dynamic.register.properties[0].password=**********
datasource.dynamic.register.properties[0].driver-class-name=com.mysql.cj.jdbc.Driver
# 第二个数据源
datasource.dynamic.register.properties[1].key=forecast
datasource.dynamic.register.properties[1].url=jdbc:mysql://127.0.0.1:3306/forecast
datasource.dynamic.register.properties[1].username=root
datasource.dynamic.register.properties[1].password=**********
datasource.dynamic.register.properties[1].driver-class-name=com.mysql.cj.jdbc.Driver
/** DynamicDataSourceContextHolder.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* 用来存储数据源的key
*/
public class DynamicDataSourceContextHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 存储已经注册的数据源的key,所有配置的数据源
*/
public static List<String> dataSourceIds = new ArrayList<>();
/**
* 线程级别的私有变量,当前线程的数据源key
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
public static String getDataSourceRouterKey () {
return HOLDER.get();
}
public static void setDataSourceRouterKey (String dataSourceRouterKey) {
logger.info("切换至{}数据源", dataSourceRouterKey);
HOLDER.set(dataSourceRouterKey);
}
/**
* 设置数据源之前一定要先移除
*/
public static void removeDataSourceRouterKey () {
HOLDER.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId 数据源ID
* @return 是否存在Datasource
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
/** DynamicRoutingDataSource.java **/
package com.sinbxeunha.josechan.dynamicDataSource.register;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceContextHolder;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceProperties;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceRegisterProperties;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动态数据源注册
* 实现 ImportBeanDefinitionRegistrar 实现数据源注册
* 实现 EnvironmentAware 用于读取application.yml配置
*/
@Component
public class DynamicDataSourceRegister implements InitializingBean {
/** 自动注入配置类 */
@Autowired
DynamicDataSourceRegisterProperties dynamicDataSourceRegisterProperties;
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
/**
* 别名
*/
private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
static {
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
}
/**
* 存储我们注册的数据源
*/
private final Map<Object, Object> customDataSources = new HashMap<Object, Object>();
/** 记录默认数据源 */
private DataSource defaultDatasource = null;
/**
* 继承自InitializingBean,对象生成后自动执行
* @throws Exception 异常
*/
@Override
public void afterPropertiesSet() throws Exception {
List<DynamicDataSourceProperties> properties = dynamicDataSourceRegisterProperties.getProperties();
String defaultKey = dynamicDataSourceRegisterProperties.getDefaultKey();
for (DynamicDataSourceProperties property : properties){
DataSourceBuilder<?> builder = property.initializeDataSourceBuilder();
DataSource dataSource = builder.type(property.getType()).build();
customDataSources.put(property.getKey(), dataSource);
DynamicDataSourceContextHolder.dataSourceIds.add(property.getKey());
if(property.getKey().equals(defaultKey)){
defaultDatasource = dataSource;
}
}
}
public Map<Object, Object> getCustomDataSources() {
return customDataSources;
}
public DataSource getDefaultDatasource() {
return defaultDatasource;
}
}
/** DynamicRoutingDataSource.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源类,继承了AbstractRoutingDataSource,实现其中的determineCurrentLookupKey
* 该方法会在每次查询前被调用,用于获取数据源
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
logger.info("当前数据源是:{}", dataSourceName);
return DynamicDataSourceContextHolder.getDataSourceRouterKey();
}
}
网友评论