iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >SpringBoot中怎么利用AOP构建多数据源
  • 803
分享到

SpringBoot中怎么利用AOP构建多数据源

2023-06-16 13:06:57 803人浏览 八月长安
摘要

SpringBoot中怎么利用aop构建多数据源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。当在业务层需要涉及到查询多种同数据库的场景下,我们通常需要在执行sql的时候动

SpringBoot中怎么利用aop构建多数据源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

当在业务层需要涉及到查询多种同数据库的场景下,我们通常需要在执行sql的时候动态指定对应的datasource。

spring的AbstractRoutingDataSource则正好为我们提供了这一功能点,下边我将通过一个简单的基于springboot+aop的案例来实现如何通过自定义注解切换不同的数据源进行读数据操作,同时也将结合部分源码的内容进行讲解。

首先我们需要自定义一个专门用于申明当前java应用程序所需要使用到哪些数据源信息:

package mutidatasource.annotation;  import mutidatasource.config.DataSourceConfigReGISter;  import mutidatasource.enums.SupportDatasourceEnum;  import org.springframework.context.annotation.Import;  import org.springframework.stereotype.Component;  import java.lang.annotation.*;    @Target({ElementType.METHOD,ElementType.TYPE})  @Retention(RetentionPolicy.RUNTIME)  @Documented  @Import(DataSourceConfigRegister.class)  public @interface AppDataSource {      SupportDatasourceEnum[] datasourceType();  }

这里为了方便,我将测试中使用的数据源地址都配置在来enum里面,如果后边需要灵活处理的话,可以将这些配置信息抽取出来放在一些配置中心上边。

package mutidatasource.enums;  import lombok.AllArgsConstructor;  import lombok.Getter;  import lombok.NoArgsConstructor;    @AllArgsConstructor  @Getter  public enum SupportDatasourceEnum {      PROD_DB("jdbc:mysql://localhost:3306/db-prod?useUnicode=true&characterEncoding=utf8","root","root","db-prod"),     DEV_DB("jdbc:Mysql://localhost:3306/db-dev?useUnicode=true&characterEncoding=utf8","root","root","db-dev"),      PRE_DB("jdbc:mysql://localhost:3306/db-pre?useUnicode=true&characterEncoding=utf8","root","root","db-pre");      String url;      String username;      String passWord;      String databaseName;      @Override      public String toString() {          return super.toString().toLowerCase();      }  }

之所以要创建这个@AppDataSource注解,是要在springboot的启动类上边进行标注:

package mutidatasource;  import mutidatasource.annotation.AppDataSource;  import mutidatasource.enums.SupportDatasourceEnum;  import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;    @SpringBootApplication  @AppDataSource(datasourceType = {SupportDatasourceEnum.DEV_DB, SupportDatasourceEnum.PRE_DB, SupportDatasourceEnum.PROD_DB})  public class SpringApplicationDemo {      public static void main(String[] args) {          SpringApplication.run(SpringApplicationDemo.class);      }  }

借助springboot的ImportSelector 自定义一个注册器来获取启动类头部的注解所指定的数据源类型:

package mutidatasource.config;  import lombok.extern.slf4j.Slf4j;  import mutidatasource.annotation.AppDataSource;  import mutidatasource.core.DataSourceContextHolder;  import mutidatasource.enums.SupportDatasourceEnum;  import org.springframework.context.annotation.ImportSelector;  import org.springframework.core.annotation.AnnotationAttributes;  import org.springframework.core.type.AnnotationMetadata;  import org.springframework.stereotype.Component;    @Slf4j  @Component  public class DataSourceConfigRegister implements ImportSelector {      @Override      public String[] selectImports(AnnotationMetadata annotationMetadata) {          AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(AppDataSource.class.getName()));          System.out.println("#######  datasource import #######");          if (null != attributes) {              Object object = attributes.get("datasourceType");              SupportDatasourceEnum[] supportDatasourceEnums = (SupportDatasourceEnum[]) object;              for (SupportDatasourceEnum supportDatasourceEnum : supportDatasourceEnums) {                  DataSourceContextHolder.aDDDatasource(supportDatasourceEnum);              }          }          return new String[0];      }  }

好的,现在我们已经能够获取到对应的数据源类型信息了,这里你会看到一个叫做DataSourceContextHolder的角色。这个对象主要是用于对每个请求线程的数据源信息做统一的分配和管理。

在多并发场景下,为了防止不同线程请求的数据源出现“互窜”情况,通常我们都会使用到threadlocal来做处理。为每一个线程都分配一个指定的,属于其内部的副本变量,当当前线程结束之前,记得将对应的线程副本也进行销毁。

package mutidatasource.core;  import mutidatasource.enums.SupportDatasourceEnum;  import java.util.HashSet;    public class DataSourceContextHolder {      private static final HashSet<SupportDatasourceEnum> dataSourceSet = new HashSet<>();      private static final ThreadLocal<String> databaseHolder = new ThreadLocal<>();      public static void setDatabaseHolder(SupportDatasourceEnum supportDatasourceEnum) {          databaseHolder.set(supportDatasourceEnum.toString());     }            public static String getDatabaseHolder() {          return databaseHolder.get();      }            public static void addDatasource(SupportDatasourceEnum supportDatasourceEnum) {          dataSourceSet.add(supportDatasourceEnum);      }            public static HashSet<SupportDatasourceEnum> getDataSourceSet() {          return dataSourceSet;      }            public static void clear() {          databaseHolder.remove();      }  }

spring内部的AbstractRoutingDataSource动态路由数据源里面有一个抽象方法叫做

determineCurrentLookupKey,这个方法适用于提供给开发者自定义对应数据源的查询key。

package mutidatasource.core;  import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;    public class DynamicDataSource extends AbstractRoutingDataSource {      @Override      protected Object determineCurrentLookupKey() {          String dataSource = DataSourceContextHolder.getDatabaseHolder();          return dataSource;      }  }

这里我使用的druid数据源,所以配置数据源的配置类如下:这里面我默认该应用配置类PROD数据源,用于测试使用。

package mutidatasource.core;  import com.alibaba.druid.pool.DruidDataSource;  import lombok.extern.slf4j.Slf4j;  import mutidatasource.enums.SupportDatasourceEnum;  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Primary;  import org.springframework.stereotype.Component;  import javax.sql.DataSource;  import java.util.HashMap;  import java.util.HashSet;    @Slf4j  @Component  public class DynamicDataSourceConfiguration {      @Bean      @Primary      @ConditionalOnMissingBean      public DataSource dataSource() {          System.out.println("init datasource");          DynamicDataSource dynamicDataSource = new DynamicDataSource();          //设置原始数据源          HashMap<Object, Object> dataSourcesMap = new HashMap<>();          HashSet<SupportDatasourceEnum> dataSet = DataSourceContextHolder.getDataSourceSet();          for (SupportDatasourceEnum supportDatasourceEnum : dataSet) {              DataSource dataSource = this.createDataSourceProperties(supportDatasourceEnum);              dataSourcesMap.put(supportDatasourceEnum.toString(), dataSource);         }          dynamicDataSource.setTargetDataSources(dataSourcesMap);          dynamicDataSource.setDefaultTargetDataSource(createDataSourceProperties(SupportDatasourceEnum.PRE_DB));          return dynamicDataSource;      }      private synchronized DataSource createDataSourceProperties(SupportDatasourceEnum supportDatasourceEnum) {          DruidDataSource druidDataSource = new DruidDataSource();          druidDataSource.setUrl(supportDatasourceEnum.getUrl());          druidDataSource.setUsername(supportDatasourceEnum.getUsername());          druidDataSource.setPassword(supportDatasourceEnum.getPassword());          //具体配置          druidDataSource.setMaxActive(100);          druidDataSource.setInitialSize(5);          druidDataSource.setMinIdle(1);          druidDataSource.setMaxWait(30000);          //间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒          druidDataSource.setTimeBetweenConnectErrORMillis(60000);          return druidDataSource;      }  }

好了现在一个基础的数据源注入已经可以了,那么我们该如何借助注解来实现动态切换数据源的操作呢?

为此,我设计了一个叫做UsingDataSource的注解,通过利用该注解来识别当前线程所需要使用的数据源操作:

package mutidatasource.annotation;  import mutidatasource.enums.SupportDatasourceEnum;  import java.lang.annotation.*;    @Target({ElementType.METHOD,ElementType.TYPE})  @Retention(RetentionPolicy.RUNTIME)  @Documented  public @interface UsingDataSource {      SupportDatasourceEnum type()  ;  }

然后,借助了spring的aop来做切面拦截:

package mutidatasource.core;  import lombok.extern.slf4j.Slf4j;  import mutidatasource.annotation.UsingDataSource;  import org.aspectj.lang.JoinPoint;  import org.aspectj.lang.ProceedingJoinPoint;  import org.aspectj.lang.Signature;  import org.aspectj.lang.annotation.*;  import org.aspectj.lang.reflect.MethodSignature;  import org.springframework.context.annotation.Configuration;  import org.springframework.core.annotation.AnnotationUtils;  import org.springframework.core.annotation.Order;  import org.springframework.stereotype.Component;  import java.lang.reflect.Method;  import java.util.Arrays;    @Slf4j  @Aspect  @Configuration  public class DataSourceAspect {      public DataSourceAspect(){          System.out.println("this is init");      }      @Pointcut("@within(mutidatasource.annotation.UsingDataSource) || " +              "@annotation(mutidatasource.annotation.UsingDataSource)")      public void pointCut(){      }      @Before("pointCut() && @annotation(usingDataSource)")      public void doBefore(UsingDataSource usingDataSource){          log.debug("select dataSource---"+usingDataSource.type());          DataSourceContextHolder.setDatabaseHolder(usingDataSource.type());     }      @After("pointCut()")      public void doAfter(){          DataSourceContextHolder.clear();      }  }

测试类如下所示:

package mutidatasource.controller;  import lombok.extern.slf4j.Slf4j;  import mutidatasource.annotation.UsingDataSource;  import mutidatasource.enums.SupportDatasourceEnum;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.jdbc.core.JdbcTemplate;  import org.springframework.WEB.bind.annotation.GetMapping;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.RestController;    @RestController  @RequestMapping(value = "/test")  @Slf4j  public class TestController {      @Autowired      private JdbcTemplate jdbcTemplate;      @GetMapping(value = "/testDev")      @UsingDataSource(type=SupportDatasourceEnum.DEV_DB)      public void testDev() {          showData();      }      @GetMapping(value = "/testPre")      @UsingDataSource(type=SupportDatasourceEnum.PRE_DB)      public void testPre() {          showData();      }      private void showData() {          jdbcTemplate.queryForList("select * from test1").forEach(row -> log.info(row.toString()));      }  }

最后 启动springboot服务,通过使用注解即可测试对应功能。

关于AbstractRoutingDataSource 动态路由数据源的注入原理,

可以看到这个内部类里面包含了多种用于做数据源映射的map数据结构

SpringBoot中怎么利用AOP构建多数据源

在该类的最底部,有一个determineCurrentLookupKey函数,也就是上边我们所提及的使用于查询当前数据源key的方法。

具体代码如下:

    protected DataSource determineTargetDataSource() {        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");        //这里面注入我们当前线程使用的数据源        Object lookupKey = determineCurrentLookupKey();        //在初始化数据源的时候需要我们去给resolvedDataSources进行注入        DataSource dataSource = this.resolvedDataSources.get(lookupKey);        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {            dataSource = this.resolvedDefaultDataSource;        }        if (dataSource == null) {            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");        }        return dataSource;    }        @Nullable    protected abstract Object determineCurrentLookupKey();

而在该类的afterPropertiesSet里面,又有对于初始化数据源的注入操作,这里面的targetDataSources 正是上文中我们对在初始化数据源时候注入的信息。 

@Override      public void afterPropertiesSet() {          if (this.targetDataSources == null) {              throw new IllegalArgumentException("Property 'targetDataSources' is required");          }          this.resolvedDataSources = new HashMap<>(this.targetDataSources.size());          this.targetDataSources.forEach((key, value) -> {              Object lookupKey = resolveSpecifiedLookupKey(key);              DataSource dataSource = resolveSpecifiedDataSource(value);              this.resolvedDataSources.put(lookupKey, dataSource);          });          if (this.defaultTargetDataSource != null) {              this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);          }      }

关于SpringBoot中怎么利用AOP构建多数据源问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网精选频道了解更多相关知识。

--结束END--

本文标题: SpringBoot中怎么利用AOP构建多数据源

本文链接: https://www.lsjlt.com/news/283915.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SpringBoot中怎么利用AOP构建多数据源
    SpringBoot中怎么利用AOP构建多数据源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。当在业务层需要涉及到查询多种同数据库的场景下,我们通常需要在执行sql的时候动...
    99+
    2023-06-16
  • springboot中如何利用mybatis-plus配置多数据源
    这篇文章主要介绍“springboot中如何利用mybatis-plus配置多数据源”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“springboot中如何利用mybatis-plus配置多数据源”...
    99+
    2023-06-08
  • SpringBoot如何搭建多数据源
    这篇文章主要为大家展示了“SpringBoot如何搭建多数据源”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“SpringBoot如何搭建多数据源”这篇文章吧。首先我们建立两个数据库(可以不在同一...
    99+
    2023-06-21
  • 怎么在SpringBoot中配置多数据源
    本篇文章为大家展示了怎么在SpringBoot中配置多数据源,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。多数据源配置首先是配置文件这里采用yml配置文件,其他类型配置文件同理我配置了两个数据源,一...
    99+
    2023-06-14
  • springboot怎么配置多数据源
    在Spring Boot中配置多个数据源可以通过以下步骤来实现: 在pom.xml文件中添加Spring Boot对多数据源的支...
    99+
    2023-10-23
    springboot
  • 怎么使用SpringBoot配置多数据源
    这篇文章主要介绍了怎么使用SpringBoot配置多数据源的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用SpringBoot配置多数据源文章都会有所收获,下面我们一起来看看吧。1. 引入jar包pom....
    99+
    2023-06-29
  • SpringBoot利用自定义注解实现多数据源
    目录前置学习数据准备行动起来前置学习 需要了解 注解、Aop、SpringBoot整合Mybatis的使用。 数据准备 基础项目代码:https://gitee.com/J_look...
    99+
    2022-11-13
    SpringBoot 自定义注解 多数据源 SpringBoot多数据源
  • SpringBoot怎么使用druid配置多数据源
    这篇“SpringBoot怎么使用druid配置多数据源”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“SpringBoot怎...
    99+
    2023-07-05
  • Spring多数据源AOP动态切换怎么实现
    这篇文章主要讲解了“Spring多数据源AOP动态切换怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring多数据源AOP动态切换怎么实现”吧!一:新增多数据源类public c...
    99+
    2023-06-04
  • SpringBoot中怎么使用Druid数据源
    SpringBoot中怎么使用Druid数据源,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。数据访问之Druid数据源的使用说明:该数据源Druid,使用自定义方式实现,后...
    99+
    2023-06-20
  • SpringBoot多数据源切换怎么实现
    本篇内容主要讲解“SpringBoot多数据源切换怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot多数据源切换怎么实现”吧!配置文件(YML)spring: ...
    99+
    2023-06-30
  • SpringBoot搭建多数据源的实现方法
    首先我们建立两个数据库(可以不在同一台电脑上): multiple_order: DROP DATABASE IF EXISTS `multiple_order`; CREATE...
    99+
    2024-04-02
  • springboot中如何配置多数据源
    这期内容当中小编将会给大家带来有关springboot中如何配置多数据源,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、建库建表1.1 创建数据库db1和数据库db21.2 在数据库db1中创建表db1...
    99+
    2023-06-15
  • 利用SpringBoot实现多数据源的两种方式总结
    目录前言基于dynamic-datasource实现多数据源dynamic-datasource介绍dynamic-datasource的相关约定 引入dynamic-da...
    99+
    2024-04-02
  • springboot中怎么配置数据源
    在Spring Boot中配置数据源有以下几种方式:1. 使用默认的数据源配置:Spring Boot提供了默认的数据源配置,只需要...
    99+
    2023-10-27
    springboot
  • springboot怎么利用aop实现接口异步
    小编给大家分享一下springboot怎么利用aop实现接口异步,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、前言在项目中发现有接口(excel导入数据)处理数据需要耗时比较长的时间,是因为数据量比较大,同时数据的校验...
    99+
    2023-06-22
  • SpringBoot怎么实现多数据源的切换
    这篇文章主要介绍“SpringBoot怎么实现多数据源的切换”,在日常操作中,相信很多人在SpringBoot怎么实现多数据源的切换问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringBoot怎么实现多...
    99+
    2023-06-29
  • 多数据源怎么利用spring boot进行配置
    本篇文章给大家分享的是有关多数据源怎么利用spring boot进行配置,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。多数据源配置创建一个Spring配置类,定义两个DataS...
    99+
    2023-05-31
    springboot 多数据源
  • springboot 中整合mybatis多数据源不使用JPA
    目录前言:创建一个springboot项目项目创建成功了,那么开始说下整合mybatis上面的配置可以直接使用如果上面的配置都完成的话那么我们来自动生成一下在下图两个数据源完全生成好...
    99+
    2024-04-02
  • springboot 中怎么配置DRUID数据源
    本篇文章为大家展示了springboot 中怎么配置DRUID数据源,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.修改pom.xml<dependency>  &...
    99+
    2023-06-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作