动态数据源切换的底层原理

it2023-09-01  72

简单介绍

实现数据源切换的功能就是自定义一个类扩展AbstractRoutingDataSource抽象类,其实相当于数据源的路由中介,可以实现在项目运行时。根据相应的key值切换到对应的DataSource上。

DynamicDataSource类
package com.picc.agricultural.service; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { /**调用流程 * public Connection getConnection() throws SQLException { return this.determineTargetDataSource().getConnection(); } 调用从AbstractRoutingDataSource继承的determineTargetDataSource方法 protected DataSource determineTargetDataSource() { 调用本类中的determineCurrentLookupKey,从threadlocal中将datasouce的key值取出 Object lookupKey = this.determineCurrentLookupKey(); 根据key拿到真正的dataSource DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey); } 如果没有拿到,就是用默认的datasource */ //从当前线程中取出放进去的dataource的键值 protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); } }
DataSourceHolder 类
public class DataSourceHolder { private final static ThreadLocal<String> datasources = new ThreadLocal<String>(); /** * 设置已知名字的数据源 * @param source */ public static void setDatasources(String source) { datasources.set(source); } /** * 获取当前正在使用的数据源的名字 * @return */ public static String getDataSource() { return datasources.get(); } /** * 清空数据源 */ public static void clearDataSources() { datasources.remove(); } }
TargetDataSource类
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { public String value() default ""; }
DataSourceConfig 类
@Component @ConfigurationProperties(prefix = "spring.datasource") @Data public class DataSourceConfig { private List<DataSourceModel> jdbc; public Map<Object,Object> getDataSourceMap() { Map<Object, Object> map = new HashMap<>(); if (jdbc!= null && jdbc.size()>0) { for (int i = 0; i < jdbc.size(); i++) { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(jdbc.get(i).getDriverClassName()); druidDataSource.setUrl(jdbc.get(i).getUrl()); druidDataSource.setUsername(jdbc.get(i).getUsername()); druidDataSource.setPassword(jdbc.get(i).getPassword()); druidDataSource.setDefaultReadOnly(jdbc.get(i).isDefaultReadOnly()); map.put(jdbc.get(i).getConnname(),druidDataSource); } } return map; } @Bean public DynamicDataSource dynamicData() { Map<Object, Object> targetDataSources= getDataSourceMap(); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(targetDataSources.values().toArray()[0]); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicData) throws Exception{ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicData); return sqlSessionFactoryBean.getObject(); } }
DataSourceAspect 切面
@Component @Aspect public class DataSourceAspect { @Before("@annotation(TargetDataSource)") public void before(JoinPoint joinPoint){ TargetDataSource annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(TargetDataSource.class); DataSourceHolder.setDatasources(annotation.value()); } @After("@annotation(TargetDataSource)") public void after(){ DataSourceHolder.clearDataSources(); } }

使用的时候,将@TargetDataSource (‘DB1’)放在dao层方法上就可以

AbstractRoutingDataSource核心方法
protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = this.determineCurrentLookupKey(); DataSource 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 + "]"); } else { return dataSource; } }
最新回复(0)