可以看到AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。
因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。
第一步:创建一个DynamicDataSource的类,继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,代码如下:

1 public class DynamicDataSource extends AbstractRoutingDataSource {
2
3 @Override
4 protected Object determineCurrentLookupKey() {
5 // 从自定义的位置获取数据源标识
6 return DynamicDataSourceHolder.getDataSource();
7 }
8
9 }

第二步:创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识,代码如下:

 1 public class DynamicDataSourceHolder {
2 /**
3 * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
4 */
5 private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
6
7 public static String getDataSource() {
8 return THREAD_DATA_SOURCE.get();
9 }
10
11 public static void setDataSource(String dataSource) {
12 THREAD_DATA_SOURCE.set(dataSource);
13 }
14
15 public static void clearDataSource() {
16 THREAD_DATA_SOURCE.remove();
17 }
18
19 }

第三步:配置多个数据源和第一步里创建的DynamicDataSource的bean,简化的配置如下:

 1 <!--创建数据源1,连接数据库db1 -->
2 <bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
3 <property name="driverClassName" value="${db1.driver}" />
4 <property name="url" value="${db1.url}" />
5 <property name="username" value="${db1.username}" />
6 <property name="password" value="${db1.password}" />
7 </bean>
8 <!--创建数据源2,连接数据库db2 -->
9 <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
10 <property name="driverClassName" value="${db2.driver}" />
11 <property name="url" value="${db2.url}" />
12 <property name="username" value="${db2.username}" />
13 <property name="password" value="${db2.password}" />
14 </bean>
15 <!--创建数据源3,连接数据库db3 -->
16 <bean id="dataSource3" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
17 <property name="driverClassName" value="${db3.driver}" />
18 <property name="url" value="${db3.url}" />
19 <property name="username" value="${db3.username}" />
20 <property name="password" value="${db3.password}" />
21 </bean>
22
23 <bean id="dynamicDataSource" class="com.test.context.datasource.DynamicDataSource">
24 <property name="targetDataSources">
25 <map key-type="java.lang.String">
26 <!-- 指定lookupKey和与之对应的数据源 -->
27 <entry key="dataSource1" value-ref="dataSource1"></entry>
28 <entry key="dataSource2" value-ref="dataSource2"></entry>
29 <entry key="dataSource3 " value-ref="dataSource3"></entry>
30 </map>
31 </property>
32 <!-- 这里可以指定默认的数据源 最好不要指定,指定后有坑-->
33 <property name="defaultTargetDataSource" ref="dataSource1" />
34 </bean>

到这里已经可以使用多数据源了,在操作数据库之前只要DynamicDataSourceHolder.setDataSource("dataSource2")即可切换到数据源2并对数据库db2进行操作了。

示例代码如下:

 1 @Service
2 public class DataServiceImpl implements DataService {
3 @Autowired
4 private DataMapper dataMapper;
5
6 @Override
7 public List<Map<String, Object>> getList1() {
8 // 没有指定,则默认使用数据源1
9 return dataMapper.getList1();
10 }
11
12 @Override
13 public List<Map<String, Object>> getList2() {
14 // 指定切换到数据源2
15 DynamicDataSourceHolder.setDataSource("dataSource2");
16 return dataMapper.getList2();
17 }
18
19 @Override
20 public List<Map<String, Object>> getList3() {
21 // 指定切换到数据源3
22 DynamicDataSourceHolder.setDataSource("dataSource3");
23 return dataMapper.getList3();
24 }
25 }

--------------------------------------------------------------------------------------华丽的分割线--------------------------------------------------------------------------------------------------

但是问题来了,如果每次切换数据源时都调用DynamicDataSourceHolder.setDataSource("xxx")就显得十分繁琐了,而且代码量大了很容易会遗漏,后期维护起来也比较麻烦。能不能直接通过注解的方式指定需要访问的数据源呢,比如在dao层使用@DataSource("xxx")就指定访问数据源xxx?当然可以!前提是,再加一点额外的配置^_^。
首先,我们得定义一个名为DataSource的注解,代码如下:

1 @Target({ TYPE, METHOD })
2 @Retention(RUNTIME)
3 public @interface DataSource {
4 String value();
5 }

然后,定义AOP切面以便拦截所有带有注解@DataSource的方法,取出注解的值作为数据源标识放到DynamicDataSourceHolder的线程变量中:

 1 public class DataSourceAspect {
2
3 /**
4 * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
5 *
6 * @param point
7 * @throws Exception
8 */
9 public void intercept(JoinPoint point) throws Exception {
10 Class<?> target = point.getTarget().getClass();
11 MethodSignature signature = (MethodSignature) point.getSignature();
12 // 默认使用目标类型的注解,如果没有则使用其实现接口的注解
13 for (Class<?> clazz : target.getInterfaces()) {
14 resolveDataSource(clazz, signature.getMethod());
15 }
16 resolveDataSource(target, signature.getMethod());
17 }
18
19 /**
20 * 提取目标对象方法注解和类型注解中的数据源标识
21 *
22 * @param clazz
23 * @param method
24 */
25 private void resolveDataSource(Class<?> clazz, Method method) {
26 try {
27 Class<?>[] types = method.getParameterTypes();
28 // 默认使用类型注解
29 if (clazz.isAnnotationPresent(DataSource.class)) {
30 DataSource source = clazz.getAnnotation(DataSource.class);
31 DynamicDataSourceHolder.setDataSource(source.value());
32 }
33 // 方法注解可以覆盖类型注解
34 Method m = clazz.getMethod(method.getName(), types);
35 if (m != null && m.isAnnotationPresent(DataSource.class)) {
36 DataSource source = m.getAnnotation(DataSource.class);
37 DynamicDataSourceHolder.setDataSource(source.value());
38 }
39 } catch (Exception e) {
40 System.out.println(clazz + ":" + e.getMessage());
41 }
42 }
43
44 }

最后在spring配置文件中配置拦截规则就可以了,比如拦截service层或者dao层的所有方法:

1 <bean id="dataSourceAspect" class="com.test.context.datasource.DataSourceAspect" />
2 <aop:config>
3 <aop:aspect ref="dataSourceAspect">
4 <!-- 拦截所有service方法 -->
5 <aop:pointcut id="dataSourcePointcut" expression="execution(* com.test.*.dao.*.*(..))"/>
6 <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
7 </aop:aspect>
8 </aop:config>

OK,这样就可以直接在类或者方法上使用注解@DataSource来指定数据源,不需要每次都手动设置了。

示例代码如下:

 1 @Service
2 // 默认DataServiceImpl下的所有方法均访问数据源1
3 @DataSource("dataSource1")
4 public class DataServiceImpl implements DataService {
5 @Autowired
6 private DataMapper dataMapper;
7
8 @Override
9 public List<Map<String, Object>> getList1() {
10 // 不指定,则默认使用数据源1
11 return dataMapper.getList1();
12 }
13
14 @Override
15 // 覆盖类上指定的,使用数据源2
16 @DataSource("dataSource2")
17 public List<Map<String, Object>> getList2() {
18 return dataMapper.getList2();
19 }
20
21 @Override
22 // 覆盖类上指定的,使用数据源3
23 @DataSource("dataSource3")
24 public List<Map<String, Object>> getList3() {
25 return dataMapper.getList3();
26 }
27 }

提示:注解@DataSource既可以加在方法上,也可以加在接口或者接口的实现类上,优先级别:方法>实现类>接口。也就是说如果接口、接口实现类以及方法上分别加了@DataSource注解来指定数据源,则优先以方法上指定的为准。

spring 配置多数据源 (可行)的更多相关文章

  1. Spring配置c3p0数据源时出错报:java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector

    今天在使用Spring配置c3p0数据源时,使用的数据库是mysql,服务器是tomcat,运行时报了一个 java.lang.NoClassDefFoundError: com/mchange/v2 ...

  2. Spring配置动态数据源-读写分离和多数据源

    在现在互联网系统中,随着用户量的增长,单数据源通常无法满足系统的负载要求.因此为了解决用户量增长带来的压力,在数据库层面会采用读写分离技术和数据库拆分等技术.读写分离就是就是一个Master数据库,多 ...

  3. Spring配置多数据源错误总结

    由于系统需要调用多个数据源包含mysql,sqlServe和Oracle,所以要在Spring的xml文件中配置多数据源,一下是配置过程中常见的错误: 1.配置的是mysql的数据源,却报oracle ...

  4. Spring配置DataSource数据源

    在Spring框架中有例如以下3种获得DataSource对象的方法: 1.从JNDI获得DataSource. 2.从第三方的连接池获得DataSource. 3.使用DriverManagerDa ...

  5. 使用Spring配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考.关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!-- ...

  6. spring配置多数据源——mybatis

    这篇文章是配置mybatis多数据源文章,如果是hibernate的话也是没什么影响,配置都是差不多的. 在这家公司上班差不多一星期了,不小心点开配置文件一看这项目配置了两个数据源,蒙了. 之后上网查 ...

  7. Spring 配置JNDI数据源

    1.Spring 提供的JNDI调用类. 2.使用weblogic进行部署项目,所以使用WebLogicNativeJdbcExtrator类进行配置. 3.配置完数据源后配置sessionFacto ...

  8. 使用 Spring 配置动态数据源实现读写分离

    关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!--读数据源配置--><bean id="readData ...

  9. 阿里P7教你如何使用 Spring 配置动态数据源实现读写分离

    最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考. 关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 <!- ...

  10. jpa+spring配置多数据源

    property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test?useU ...

随机推荐

  1. 《c程序设计语言》读书笔记-5.5-指针实现strncpy,strncat,strncmp

    #include <stdio.h> #include <math.h> #include <stdlib.h> #include <string.h> ...

  2. shell-总结【摘录】

    阅读目录 1. Shell简介 2. 几种常见的Shell 3. 编译型语言和解释型语言的区别 4. 什么时候使用Shell? 5. 第一个Shell脚本 6. Shell变量 7.Shell特殊变量 ...

  3. [从hzwer神犇那翻到的模拟赛题] 合唱队形

    [问题描述] 学校要进行合唱比赛了,于是班主任小刘准备给大家排个队形. 他首先尝试排成m1行,发现最后多出来a1个同学:接着他尝试排成m2行,发现最后多出来a2个同学,……,他尝试了n种排队方案,但每 ...

  4. linux内核分析之进程地址空间【转】

    转自:http://blog.csdn.net/bullbat/article/details/7106094 版权声明:本文为博主原创文章,未经博主允许不得转载. 本文主要介绍linux内核中进程地 ...

  5. 12.OpenStack镜像和存储服务配置

    配置镜像服务 编辑 /etc/glance/glance-api.conf与/etc/glance/glance-registry.conf添加以下内容 [DEFAULT] notification_ ...

  6. 10.OpenStack块存储服务

    添加块存储服务 安装和配置控制器节点 创建数据库 mysql -uroot -ptoyo123 CREATE DATABASE cinder; GRANT ALL PRIVILEGES ON cind ...

  7. VS2008中的配置文件app.config简单小结

    应用程序的配置文件用于读取和保存简单的本地数据,vs中新增配置文件可以直接在项目的”属性“-”设置“里添加,添加后在项目的Properties文件夹会多出一组两个文件:Settings.setting ...

  8. bootstrap只有遮罩层没有对话框的解决方法

    前端很差很差,猜测应该是各种js冲突的问题,换了一个jquery或bootstrap版本的不兼容. https://blog.csdn.net/Pabebe/article/details/70230 ...

  9. Python的程序结构[2] -> 类/Class[3] -> 内建类与内建函数

    内建类与内建函数的区分 / Distinction of Built-in Type and Function 对于 Python,有许多可以不需要定义或引用就可以使用的函数(类)(参考内建模块),诸 ...

  10. 动态读取cron表达式

    项目中在使用任务调度时往往会用到cron表达式,比如每五分钟执行一次,每天12点执行一次,每周四凌晨1点执行一次等.但是如果将cron表达式写死,往往不利于测试.解决方案:我们可以将cron表达式写入 ...