MyException - 我的异常网
当前位置:我的异常网» 软件架构设计 » 基于注脚的Spring多数据源配置和使用

基于注脚的Spring多数据源配置和使用

www.MyException.Cn  网友分享于:2013-03-06  浏览:0次
基于注解的Spring多数据源配置和使用
https://www.cnblogs.com/liujiduo/p/5004691.html

不仅讲了 多数据源 , 甚至讲了自定义注解,并在spring里用pointcut aop来拦截。

引用


可以看到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>
9 </bean>
复制代码
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注解来指定数据源,则优先以方法上指定的为准。


文章评论

“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员都该阅读的书
程序员都该阅读的书
我是如何打败拖延症的
我是如何打败拖延症的
程序员应该关注的一些事儿
程序员应该关注的一些事儿
代码女神横空出世
代码女神横空出世
程序员的鄙视链
程序员的鄙视链
10个调试和排错的小建议
10个调试和排错的小建议
为什么程序员都是夜猫子
为什么程序员都是夜猫子
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
漫画:程序员的工作
漫画:程序员的工作
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
那些争议最大的编程观点
那些争议最大的编程观点
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
我的丈夫是个程序员
我的丈夫是个程序员
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
 程序员的样子
程序员的样子
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
旅行,写作,编程
旅行,写作,编程
中美印日四国程序员比较
中美印日四国程序员比较
每天工作4小时的程序员
每天工作4小时的程序员
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员和编码员之间的区别
程序员和编码员之间的区别
程序员必看的十大电影
程序员必看的十大电影
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
如何成为一名黑客
如何成为一名黑客
一个程序员的时间管理
一个程序员的时间管理
编程语言是女人
编程语言是女人
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有