Spring有一个AbstractRoutingDataSource
类,它是一个实现DataSource
的抽象类,它的getConnection()
实现基于lookup key
来查找数据源。通常我们通过在线程上绑定不同的数据源来实现。
// 动态dataSource类 public class DynamicDataSource extends AbstractRoutingDataSource { // 用于在当前线程存储当前的数据源 private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } // 返回当前线程的dataSource key @Override protected Object determineCurrentLookupKey() { return CONTEXT_HOLDER.get(); } public static void setDefaultDs() { CONTEXT_HOLDER.set("default"); } public static void setReadDs() { CONTEXT_HOLDER.set("read"); } }
这个类设置默认的数据源,与一个数据源列表,可通过在线程中去切换要使用的数据源。
创建一个自定义注解,用于在aop中使用。
// 只读DataSource @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ReadDs { }
创建处理此注解的切面:
@Aspect public class DataSourceAspect { @Before("@annotation(com.store.service.aops.ReadDs)") public void setReadDs() { System.out.println("执行setReadDs"); DynamicDataSource.setReadDs(); } }
在JavaConfig中配置DataSource和切面,不要忘了在配置文件中启用aop - @EnableAspectJAutoProxy:
@Bean public DataSource dynamicDataSource() { DataSource defaultDs = defaultDataSource(); Map<Object, Object> map = new HashMap<>(); map.put("default", defaultDs); map.put("read", readDataSource()); return new DynamicDataSource(defaultDs, map); } @Bean public DataSourceAspect dataSourceAspect() { return new DataSourceAspect(); }
在需要启用读写分离的方法上添加我们创建的@ReadDs
注解即可。