热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于自定义注解和AOP的Spring多数据源配置和使用

前一段时间研究了一下spring多数据源的配置和使用,为了后期从多个数据源拉取数据定时进行数据分析和报表统计做准备。由于之前做过的项目都是单数据源的,没

前一段时间研究了一下spring多数据源的配置和使用,为了后期从多个数据源拉取数据定时进行数据分析和报表统计做准备。由于之前做过的项目都是单数据源的,没有遇到这种场景,所以也一直没有去了解过如何配置多数据源。
后来发现其实基于spring来配置和使用多数据源还是比较简单的,因为spring框架已经预留了这样的接口可以方便数据源的切换。
先看一下spring获取数据源的源码:

可以看到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 THREAD_DATA_SOURCE = new ThreadLocal();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 2 3 4 5 6 7 8 9
10
11
12
13
14

15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30

31

32
33
34

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

示例代码如下:

1 @Service2 public class DataServiceImpl implements DataService {3 @Autowired4 private DataMapper dataMapper;5 6 @Override7 public List> getList1() {8 // 没有指定,则默认使用数据源19 return dataMapper.getList1();
10 }
11
12 @Override
13 public List> getList2() {
14 // 指定切换到数据源2
15 DynamicDataSourceHolder.setDataSource("dataSource2");
16 return dataMapper.getList2();
17 }
18
19 @Override
20 public List> 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 point7 * @throws Exception8 */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
2
3
4
5
6
7

8

9

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

示例代码如下:

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

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

转:https://www.cnblogs.com/niehaikuo/p/8627007.html



推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 合并列值-合并为一列问题需求:createtabletab(Aint,Bint,Cint)inserttabselect1,2,3unionallsel ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
author-avatar
手机点菜_748
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有