一般情况下,很多项目(spring)由于对数据库(mysql)的并发量上来之后,都会配置主从机数据库,写数据使用主机数据库,而读数据使用从机数据库。那么如何使得对主从机数据源的使用能动态根据业务切换?
由于我这边使用了maven,也用到了aspect、mysql,so附上maven依赖,方便copy。
<properties> <spring.version>4.2.2.RELEASE</spring.version> <aspectj.version>1.6.11</aspectj.version> <mysql_connect_version>5.1.35</mysql_connect_version> </properties> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql_connect_version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!--使用AspectJ方式注解需要相应的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> <!--使用AspectJ方式注解需要相应的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> </dependencies>在后面的aspect切面方法里,可以看到会给当前执行方法选择一个数据源的名称,把该名称put到执行该方法的线程的ThreadMap(通过holder),然后在继承AbstractRoutingDataSource的类中determineCurrentLookupKey方法调用holder的get方法返回当前线程选择的数据源名称。
/** * 数据源的Handler类 */ public class DataSourceHandler { // 数据源名称holder public static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>(); /** * 将读、写数据源加到dataSourceHolder中 */ public static void putDataSource(String datasource) { dataSourceHolder.set(datasource); } /** * 从holer中获取数据源字符串 */ public static String getDataSource() { return dataSourceHolder.get(); } /** * @Description: 清除数据源类型 * @param * @return void */ public static void clearDataSourceType() { dataSourceHolder.remove(); } }在spring配置文件中会注入map,该Map存储对应方法包含某些字符映射某个数据源,在切面方法中根据该Map给执行方法的线程选择数据源。
/** * 获取数据源,用于动态切换数据源 */ public class ChooseDataSource extends AbstractRoutingDataSource { public static Map<String, List<String>> map = new HashMap<String, List<String>>(); /** * 实现父类中的抽象方法,获取数据源名称 * @return */ protected Object determineCurrentLookupKey() { return DataSourceHandler.getDataSource(); } }PS:很多人可能会使用失败,原因很可能就是下面的Asprct配置的问题
/** * 切换数据源(不同方法调用不同数据源) */ @Aspect @Component @EnableAspectJAutoProxy(proxyTargetClass = true) public class DataSourceAspect { @Pointcut("execution(*.*.*(..))") public void aspect() { } /** * 配置前置通知,使用在方法aspect()上注册的切入点 */ /** *该方法主要判断执行的Dao类方法名是否包含指定字符子**串,从而选择对应设定的数据源名。 */ @Before("aspect()") public void before(JoinPoint point) { String className = point.getTarget().getClass().getName(); String method = point.getSignature().getName(); try { for (String key : ChooseDataSource.map.keySet()) { for (String type : ChooseDataSource.map.get(key)) { if (method.startsWith(type)) { DataSourceHandler.putDataSource(key); } } } } catch (Exception e) { e.printStackTrace(); } } }好了,到这里就大功告成了,体验体验效果吧。。。
