springboot基于Aop实现的动态数据源切换
- 编写AbstractRoutingDataSource的实现类,HandlerDataSource就是提供给我们动态选择数据源的数据的信息,我们这里编写一个根据当前线程来选择数据源,然后通过AOP拦截特定的注解,设置当前的数据源信息,也可以手动的设置当前的数据源,在编程的类中。
/**
* @author luhong
* @version 1.0
* @Description 获取动态数据源
* @date 10/22/2018
* @updateby
* @updatedate
* @since 1.0
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
public Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSourceKey();
}
}
- 编写DynamicDataSourceHolder.class
/**
* @author luhong
* @version 1.0
* @Description
* @date 10/22/2018
* @updateby
* @updatedate
* @since 1.0
*/
public class DynamicDataSourceHolder {
//写库对应的数据源key
private static final String READ = "read";
//读库对应的数据源key
private static final String WRITE = "write";
//使用ThreadLocal记录当前线程的数据源key
private static final ThreadLocal<String> holder = new ThreadLocal<String>();
/**
* @author: luhong
* @Description 管理所有的数据源id; 主要是为了判断数据源是否存在;
* @Date 5:08 PM 10/27/2018
* @Param
* @return:
*/
public static List<String> dataSourceIds = new ArrayList<String>();
/**
* 设置数据源key
* @param key
*/
public static void setDataSourceKey(String key) {
holder.set(key);
}
/**
* 获取数据源key
* @return
*/
public static String getDataSourceKey() {
return holder.get();
}
/**
* 标记写库
*/
public static void markRead(){
setDataSourceKey(READ);
}
/**
* 标记读库
*/
public static void markWrite(){
setDataSourceKey(WRITE);
}
//清除数据源
public static void clearDataSourceType() {
holder.remove();
}
/**
* @author: luhong
* @Description
* @Date 4:46 PM 10/24/2018
* @Param [dataSourceId]
* @return:boolean
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
- 编写DynamicDataSourceRegister.class
/**
* @author luhong
* @version 1.0
* @Description 动态数据源注册
* @date 10/24/2018
* @updateby
* @updatedate
* @since 1.0
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
/**
* 如配置文件中未指定数据源类型,使用该默认值
*/
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
private ConversionService conversionService = new DefaultConversionService();
private PropertyValues dataSourcePropertyValues;
// 默认数据源
private DataSource defaultDataSource;
private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
/**
* @author: luhong
* @Description 加载多数据源配置
* @Date 5:02 PM 10/27/2018
* @Param [environment]
* @return:void
*/
@Override
public void setEnvironment(Environment environment) {
System.out.println("DynamicDataSourceRegister.setEnvironment()");
initDefaultDataSource(environment);
initCustomDataSources(environment);
}
/**
* @author: luhong
* @Description 加载主数据源配置.
* @Date 5:03 PM 10/27/2018
* @Param [env]
* @return:void
*/
private void initDefaultDataSource(Environment env){
// 读取主数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.buz.");
Map<String, Object> dsMap = new HashMap<String, Object>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
//创建数据源;
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, env);
}
/**
* @author: luhong
* @Description 初始化更多数据源
* @Date 5:04 PM 10/27/2018
* @Param [env]
* @return:void
*/
private void initCustomDataSources(Environment env) {
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
// 多个数据源
for (String dsPrefix : dsPrefixs.split(",")) {
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, env);
}
}
/**
* @author: luhong
* @Description 创建datasource.
* @Date 5:04 PM 10/27/2018
* @Param [dsMap]
* @return:javax.sql.DataSource
*/
@SuppressWarnings("unchecked")
public DataSource buildDataSource(Map<String, Object> dsMap) {
Object type = dsMap.get("type");
if (type == null){
// 默认DataSource
type = DATASOURCE_TYPE_DEFAULT;
}
Class<? extends DataSource> dataSourceType;
try {
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driver-class-name").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType);
return factory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* @author: luhong
* @Description 为DataSource绑定更多数据
* @Date 5:05 PM 10/27/2018
* @Param [dataSource, env]
* @return:void
*/
private void dataBinder(DataSource dataSource, Environment env){
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
//false
dataBinder.setIgnoreNestedProperties(false);
//false
dataBinder.setIgnoreInvalidFields(false);
//true
dataBinder.setIgnoreUnknownFields(true);
if(dataSourcePropertyValues == null){
Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.buz-datasource").getSubProperties(".");
Map<String, Object> values = new HashMap<>(rpr);
// 排除已经设置的属性
values.remove("type");
values.remove("driver-class-name");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// 将主数据源添加到更多数据源中
targetDataSources.put("dataSource", defaultDataSource);
DynamicDataSourceHolder.dataSourceIds.add("dataSource");
// 添加更多数据源
targetDataSources.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceHolder.dataSourceIds.add(key);
}
// 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
//添加属性:AbstractRoutingDataSource.defaultTargetDataSource
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
}
}
- dao层
/** * 数据库操作方法加上注解@DataSourceAop(name = "buz-read") * 指定数据库 */ @DataSourceAop(name = "buz-read") public Integer queryIsJoin(){ }
- 增加DataSourceAop.class
/** * @author luhong * @version 1.0 * @Description * @date 10/22/2018 * @updateby * @updatedate * @since 1.0 */ @Target({ElementType.METHOD,ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataSourceAop { String name(); }