<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot 配置多數(shù)據(jù)源(Aop+注解實現(xiàn))

          共 7944字,需瀏覽 16分鐘

           ·

          2020-12-04 15:48

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          66套java從入門到精通實戰(zhàn)課程分享

          在實際項目中很多時候會涉及到多個數(shù)據(jù)庫的訪問,或者數(shù)據(jù)庫讀寫分離的形式。

          下面通過使用 Aspect+注解來實現(xiàn)mybatis的多數(shù)據(jù)源配置? ?

          動態(tài)數(shù)據(jù)源流程說明

          Spring Boot 的動態(tài)數(shù)據(jù)源,本質(zhì)上是把多個數(shù)據(jù)源存儲在一個 Map 中,當(dāng)需要使用某個數(shù)據(jù)源時,從 Map 中獲取此數(shù)據(jù)源進行處理。而在 Spring 中,已提供了抽象類?AbstractRoutingDataSource?來實現(xiàn)此功能。因此,我們在實現(xiàn)動態(tài)數(shù)據(jù)源的,只需要繼承它,實現(xiàn)自己的獲取數(shù)據(jù)源邏輯即可。動態(tài)數(shù)據(jù)源流程如下所示:

          用戶訪問應(yīng)用,在需要訪問不同的數(shù)據(jù)源時,根據(jù)自己的數(shù)據(jù)源路由邏輯,訪問不同的數(shù)據(jù)源,實現(xiàn)對應(yīng)數(shù)據(jù)源的操作。本示例中的兩數(shù)據(jù)庫的分別有一個表?t_user,表結(jié)構(gòu)一致,為便于說明,兩個表中的數(shù)據(jù)是不一樣的。

          實現(xiàn)動態(tài)數(shù)據(jù)源

          1.數(shù)據(jù)庫連接信息配置

          在application.yml中添加如下信息,可根據(jù)自己的項目進行替換相應(yīng)的prefix和連接信息。

          springboot:
          ??datasource:
          ????master:
          ??????driver-class-name:?com.mysql.cj.jdbc.Driver
          ??????jdbc-url:?jdbc:mysql://localhost:3306/spring?useUnicode=true&characherEncoding=utf-8&useSSL=true&serverTimezone=UTC
          ??????username:?root
          ??????password:?123456
          ????slave:
          ??????driver-class-name:?com.mysql.cj.jdbc.Driver
          ??????jdbc-url:?jdbc:mysql://localhost:3306/spring1?useUnicode=true&characherEncoding=utf-8&useSSL=true&serverTimezone=UTC
          ??????username:?root
          ??????password:?123456

          2.數(shù)據(jù)源配置

          根據(jù)連接信息,把數(shù)據(jù)源注入到 Spring 中,添加?DynamicDataSourceConfig?文件,配置如下:

          @Configuration
          public?class?DynamicDataSourceConfig?{
          ????@Bean(DataSourceConstants.DS_KEY_MASTER)
          ????@ConfigurationProperties(prefix?=?"springboot.datasource.master")
          ????public?DataSource?masterDataSource()?{
          ????????return?DataSourceBuilder.create().build();
          ????}

          ????@Bean(DataSourceConstants.DS_KEY_SLAVE)
          ????@ConfigurationProperties(prefix?=?"springboot.datasource.slave")
          ????public?DataSource?slaveDataSource()?{
          ????????return?DataSourceBuilder.create().build();
          ????}
          }

          DataSourceConstants類:

          public?class?DataSourceConstants?{
          ????public?final?static?String?DS_KEY_MASTER?=?"master";

          ????public?final?static?String?DS_KEY_SLAVE?=?"slave";
          }

          分別將master和slave數(shù)據(jù)源注入到spring容器中。

          3.動態(tài)數(shù)據(jù)源設(shè)置

          添加動態(tài)數(shù)據(jù)源類

          DynamicDataSource?和?DynamicDataSourceContextHolder

          import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

          public?class?DynamicDataSource?extends?AbstractRoutingDataSource?{
          ????@Override
          ????protected?Object?determineCurrentLookupKey()?{
          ????????return?DynamicDataSourceContextHolder.getContextKey();
          ????}
          }

          public?class?DynamicDataSourceContextHolder?{
          ????/*?動態(tài)數(shù)據(jù)源名稱上下文*/
          ????private?static?final?ThreadLocal?DATASOURCE_CONTEXT_KEY_HOLDER?=?new?ThreadLocal<>();

          ????/*?設(shè)置/切換數(shù)據(jù)源*/
          ????public?static?void?setContextKey(String?key)?{
          ????????System.out.println("切換數(shù)據(jù)源"+key);
          ????????DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
          ????}

          ????/*?獲取數(shù)據(jù)源名稱?*/
          ????public?static?String?getContextKey()?{
          ????????String?key?=?DATASOURCE_CONTEXT_KEY_HOLDER.get();
          ????????return?key?==?null???DataSourceConstants.DS_KEY_MASTER?:?key;
          ????}

          ????/*刪除當(dāng)前數(shù)據(jù)源名稱*/
          ????public?static?void?removeContextKey()?{
          ????????DATASOURCE_CONTEXT_KEY_HOLDER.remove();
          ????}
          }

          3.設(shè)置動態(tài)數(shù)據(jù)源為主數(shù)據(jù)源

          在前面的數(shù)據(jù)源配置文件?DynamicDataSourceConfig?中,添加以下代碼:

          @Bean
          @Primary
          public?DataSource?dynamicDataSource()?{
          ????Map?dataSourceMap?=?new?HashMap<>(2);
          ????dataSourceMap.put(DataSourceConstants.DS_KEY_MASTER,?masterDataSource());
          ????dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE,?slaveDataSource());
          ????//設(shè)置動態(tài)數(shù)據(jù)源
          ????DynamicDataSource?dynamicDataSource?=?new?DynamicDataSource();
          ????dynamicDataSource.setTargetDataSources(dataSourceMap);
          ????dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
          ????
          ????return?dynamicDataSource;
          }

          • 使用注解?Primary?優(yōu)先從動態(tài)數(shù)據(jù)源中獲取

          • 同時,需要在?DynamicDataSourceConfig?中,排除?DataSourceAutoConfiguration?的自動配置,否則 會出現(xiàn)The dependencies of some of the beans in the application context form a cycle的錯誤。在·?DynamicDataSourceConfig類上添加

            @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
            • 1

          DynamicDataSourceConfig?最終代碼如下:

          @Configuration
          @EnableAutoConfiguration(exclude?=?{?DataSourceAutoConfiguration.class?})
          public?class?DynamicDataSourceConfig?{
          ????@Bean(DataSourceConstants.DS_KEY_MASTER)
          ????@ConfigurationProperties(prefix?=?"springboot.datasource.master")
          ????public?DataSource?masterDataSource()?{
          ????????return?DataSourceBuilder.create().build();
          ????}

          ????@Bean(DataSourceConstants.DS_KEY_SLAVE)
          ????@ConfigurationProperties(prefix?=?"springboot.datasource.slave")
          ????public?DataSource?slaveDataSource()?{
          ????????return?DataSourceBuilder.create().build();
          ????}

          ????@Bean
          ????@Primary
          ????public?DataSource?dynamicDataSource()?{
          ????????Map?dataSourceMap?=?new?HashMap<>(2);
          ????????dataSourceMap.put(DataSourceConstants.DS_KEY_MASTER,?masterDataSource());
          ????????dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE,?slaveDataSource());
          ????????//?設(shè)置動態(tài)數(shù)據(jù)源
          ????????DynamicDataSource?dynamicDataSource?=?new?DynamicDataSource();
          ????????dynamicDataSource.setTargetDataSources(dataSourceMap);
          ????????dynamicDataSource.setDefaultTargetDataSource(masterDataSource());

          ????????return?dynamicDataSource;
          ????}
          }

          使用AOP選擇數(shù)據(jù)源

          1.自定義一個annotation

          DbAnnotation注解,用于mapper接口上:

          @Target({?ElementType.METHOD,?ElementType.TYPE?})
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?DbAnnotation?{
          ????/**
          ?????*?數(shù)據(jù)源名稱,默認master
          ?????*/
          ????String?value()?default?DataSourceConstants.DS_KEY_MASTER;
          }

          2.定義數(shù)據(jù)源切面

          定義數(shù)據(jù)源切面,用于攔截添加了@DbAnnotation注解的類或方法:

          添加maven依賴

          ????
          ????org.springframework.boot????
          ????spring-boot-starter-aop?


          定義切面DynamicDataSourceAspect類:

          @Aspect
          @Component
          public?class?DynamicDataSourceAspect?{
          ????//攔截DbAnnotation
          ????@Pointcut("@within(com.mj.vscodedemo.annotation.DbAnnotation)")
          ????public?void?dataSourcePointCut()?{
          ????}

          ????@Around("dataSourcePointCut()")
          ????public?Object?around(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
          ????????String?dsKey?=?this.getDSAnnotation(joinPoint).value();
          ????????DynamicDataSourceContextHolder.setContextKey(dsKey);
          ????????try?{
          ????????????return?joinPoint.proceed();
          ????????}?catch?(Exception?ex)?{
          ????????????throw?ex;
          ????????}?finally?{
          ????????????DynamicDataSourceContextHolder.removeContextKey();
          ????????}
          ????}

          ????/**
          ?????*?根據(jù)類或方法獲取數(shù)據(jù)源注解
          ?????*/
          ????private?DbAnnotation?getDSAnnotation(ProceedingJoinPoint?joinPoint)?{
          ????????//mybatis生成的代理類,所以獲取它的接口來獲取DbAnnotation注解信息
          ????????Class?targetClass?=?joinPoint.getTarget().getClass().getInterfaces()[0];
          ????????DbAnnotation?dsAnnotation?=?targetClass.getAnnotation(DbAnnotation.class);
          ????????//?先判斷類的注解,再判斷方法注解
          ????????if?(Objects.nonNull(dsAnnotation))?{
          ????????????return?dsAnnotation;
          ????????}?else?{
          ????????????MethodSignature?methodSignature?=?(MethodSignature)?joinPoint.getSignature();
          ????????????DbAnnotation?annotation?=?methodSignature.getMethod().getAnnotation(DbAnnotation.class);
          ????????????return?annotation;
          ????????}
          ????}
          }

          在mapper接口上添加@DbAnnotation注解,分別為master和slave數(shù)據(jù)源添加了個測試的mapper:

          @Mapper
          @Repository
          @DbAnnotation(DataSourceConstants.DS_KEY_MASTER)
          public?interface?UserMapper?{
          ????Integer?getUserId();
          }

          @Mapper
          @Repository
          @DbAnnotation(DataSourceConstants.DS_KEY_SLAVE)
          public?interface?UserMapper1?{
          ????Integer?getUserId();
          }

          多數(shù)據(jù)源配置已經(jīng)完成了,下面就可以進行調(diào)用測試訪問了

          測試

          添加一個controller并直接調(diào)用mapper的方法

          @RestController
          public?class?HelloController?{
          ????@Autowired
          ????private?UserMapper?userMapper;

          ????@Autowired
          ????private?UserMapper1?userMapper1;

          ????@ApiOperation(value?=?"sayHi")
          ????@GetMapping("/sayHi")
          ????public?String?sayHi()?{
          ????????int?i?=?userMapper.getUserId();
          ????????System.out.println("用戶1數(shù)量"?+?i);
          ????????int?i2?=?userMapper1.getUserId();
          ????????System.out.println("用戶2數(shù)量"?+?i2);
          ????????return?"你好啊";
          ????}
          }

          輸出如下:

          切換數(shù)據(jù)源master
          2020-11-29?22:44:58.933??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-1?-?Starting...
          2020-11-29?22:45:00.451??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-1?-?Start?completed.
          用戶1數(shù)量1
          切換數(shù)據(jù)源slave
          2020-11-29?22:45:00.664??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-2?-?Starting...
          2020-11-29?22:45:00.728??INFO?13292?---?[nio-8090-exec-1]?com.zaxxer.hikari.HikariDataSource???????:?HikariPool-2?-?Start?completed.
          用戶2數(shù)量2

          可以看到分別訪問的是master和slave的數(shù)據(jù)源。

          下面就可以進行db讀寫分離愉快的寫代碼了。



          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/mj_940620/article/details/110356785






          粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取

          ???

          ?長按上方微信二維碼?2 秒
          即可獲取資料



          感謝點贊支持下哈?

          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  爽 好紧 别夹 喷水无码 | 黄色电影视频在线精品 | 淫秽三级片中文字幕在线免费观看 | 亚洲国产另类无码日韩 | 五月婷婷六月天 |