一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringBoot AOP方式實現多數據源切換的方法

SpringBoot AOP方式實現多數據源切換的方法

2021-04-16 13:26dianwoda Java教程

本篇文章主要介紹了SpringBoot AOP方式實現多數據源切換的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

最近在做保證金余額查詢優化,在項目啟動時候需要把余額全量加載到本地緩存,因為需要全量查詢所有騎手的保證金余額,為了不影響主數據庫的性能,考慮把這個查詢走從庫。所以涉及到需要在一個項目中配置多數據源,并且能夠動態切換。經過一番摸索,完美實現動態切換,記錄一下配置方法供大家參考。

設計總體思路

Spring-Boot+AOP方式實現多數據源切換,繼承AbstractRoutingDataSource實現數據源動態的獲取,在service層使用注解指定數據源。

步驟

一、多數據源配置

在application.properties中,我們的配置是這樣的

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#主數據源
druid.master.url=jdbc:mysql://url/masterdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.master.username=xxx
druid.master.password=123
druid.master.driver-class-name=com.mysql.jdbc.Driver
druid.master.max-wait=5000
druid.master.max-active=100
druid.master.test-on-borrow=true
druid.master.validation-query=SELECT 1
 
#從數據源
druid.slave.url=jdbc:mysql://url/slavedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.slave.username=xxx
druid.slave.password=123
druid.slave.driver-class-name=com.mysql.jdbc.Driver
druid.slave.max-wait=5000
druid.slave.max-active=100
druid.slave.test-on-borrow=true
druid.slave.validation-query=SELECT 1

讀取配置

 
?
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!-- master數據源 -->
<bean primary="true" id="masterdb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <!-- 基本屬性 url、user、password -->
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="${druid.master.url}"/>
  <property name="username" value="${druid.master.username}"/>
  <property name="password" value="${druid.master.password}"/>
  <!-- 配置初始化最大 -->
  <property name="maxActive" value="${druid.master.max-active}"/>
  <!-- 配置獲取連接等待超時的時間 -->
  <property name="maxWait" value="${druid.master.max-wait}"/>
  <property name="validationQuery" value="${druid.master.validation-query}"/>
  <property name="testOnBorrow" value="${druid.master.test-on-borrow}"/>
 
</bean>
 
<!-- slave數據源 -->
<bean primary="true" id="slavedb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  <!-- 基本屬性 url、user、password -->
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="${druid.slave.url}"/>
  <property name="username" value="${druid.slave.username}"/>
  <property name="password" value="${druid.slave.password}"/>
 
  <!-- 配置初始化大小、最小、最大 -->
  <property name="maxActive" value="${druid.slave.max-active}"/>
  <!-- 配置獲取連接等待超時的時間 -->
  <property name="maxWait" value="${druid.slave.max-wait}"/>
  <property name="validationQuery" value="${druid.slave.validation-query}"/>
  <property name="testOnBorrow" value="${druid.slave.test-on-borrow}"/>
</bean>
 
<!-- 動態數據源,根據service接口上的注解來決定取哪個數據源 -->
<bean id="dataSource" class="datasource.DynamicDataSource">
  <property name="targetDataSources">
    <map key-type="java.lang.String">
      <entry key="slave" value-ref="slavedb"/>
      <entry key="master" value-ref="masterdb"/>
    </map>
  </property>
  <property name="defaultTargetDataSource" ref="masterdb"/>
</bean>
 
<!-- Spring JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="dataSource" />
</bean>
 
<!-- Spring事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
 
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
  <property name="transactionManager" ref="transactionManager"/>
</bean>
 
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2" />
 
<!-- depositdbSqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:mapper-xxdb/*Mapper*.xml" />
</bean>
 
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="xxdb.mapper"/>
  <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

二、動態數據源

spring為我們提供了AbstractRoutingDataSource,即帶路由的數據源。繼承后我們需要實現它的determineCurrentLookupKey(),該方法用于自定義實際數據源名稱的路由選擇方法,由于我們將信息保存到了ThreadLocal中,所以只需要從中拿出來即可。

 
?
1
 
2
3
4
5
6
7
8
9
10
public class DynamicDataSource extends AbstractRoutingDataSource {
  private Logger logger = LoggerFactory.getLogger(this.getClass());
 
  @Override
  protected Object determineCurrentLookupKey() {
    String dataSource = JdbcContextHolder.getDataSource();
    logger.info("數據源為{}",dataSource);
    return dataSource;
  }
}

三. 數據源動態切換類

動態數據源切換是基于AOP的,所以我們需要聲明一個AOP切面,并在切面前做數據源切換,切面完成后移除數據源名稱。

 
?
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
35
36
37
@Aspect
@Order(1//設置AOP執行順序(需要在事務之前,否則事務只發生在默認庫中)
@Component
public class DataSourceAspect {
 
  private Logger logger = LoggerFactory.getLogger(this.getClass());
  //切點
  @Pointcut("execution(* com.xxx.service.*.*(..))")
  public void aspect() { }
 
  @Before("aspect()")
  private void before(JoinPoint point) {
    Object target = point.getTarget();
    String method = point.getSignature().getName();
    Class<?> classz = target.getClass();// 獲取目標類
    Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
        .getMethod().getParameterTypes();
    try {
      Method m = classz.getMethod(method, parameterTypes);
      if (m != null && m.isAnnotationPresent(MyDataSource.class)) {
        MyDataSource data = m.getAnnotation(MyDataSource.class);
        logger.info("method :{},datasource:{}",m.getName() ,data.value().getName());
        JdbcContextHolder.putDataSource(data.value().getName());// 數據源放到當前線程中
      }
    } catch (Exception e) {
      logger.error("get datasource error ",e);
      //默認選擇master
      JdbcContextHolder.putDataSource(DataSourceType.Master.getName());// 數據源放到當前線程中
    }
 
  }
 
  @AfterReturning("aspect()")
  public void after(JoinPoint point) {
    JdbcContextHolder.clearDataSource();
  }
}

四、數據源管理類

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class JdbcContextHolder {
 
  private final static ThreadLocal<String> local = new ThreadLocal<>();
 
  public static void putDataSource(String name) {
    local.set(name);
  }
 
  public static String getDataSource() {
    return local.get();
  }
 
  public static void clearDataSource() {
    local.remove();
  }
}

五、數據源注解和枚舉

我們切換數據源時,一般都是在調用具體接口的方法前實現,所以我們定義一個方法注解,當AOP檢測到方法上有該注解時,根據注解中value對應的名稱進行切換。

 
?
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
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyDataSource {
 
  DataSourceType value();
 
}
public enum DataSourceType {
  // 主表
  Master("master"),
  // 從表
  Slave("slave");
 
  private String name;
 
  private DataSourceType(String name) {
    this.name = name;
  }
 
  public String getName() {
    return name;
  }
 
  public void setName(String name) {
    this.name = name;
  }
}

六、切點注解

由于我們的動態數據源配置了默認庫,所以如果方法是操作默認庫的可以不需要注解,如果要操作非默認數據源,我們需要在方法上添加@MyDataSource("數據源名稱")注解,這樣就可以利用AOP實現動態切換了

 
?
1
 
2
3
4
5
6
7
8
9
10
@Component
public class xxxServiceImpl {
  @Resource
  private XxxMapperExt xxxMapperExt;
 
  @MyDataSource(value= DataSourceType.Slave)
  public List<Object> getAll(){
    return xxxMapperExt.getAll();
  }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://tech.dianwoda.com/2018/03/28/spring-boot-aopfang-shi-shi-xian-duo-shu-ju-yuan-qie-huan

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲九九精品 | 俄罗斯12一15处交 | 亚洲嫩模吧粉嫩粉嫩冒白浆 | 国产精品免费_区二区三区观看 | 日韩欧美视频二区 | 日女人免费视频 | 国产精品一区牛牛影视 | 9久re在线观看视频精品 | 精品视频中文字幕 | 午夜视频一区二区三区 | 九九365资源稳定资源站 | 狠狠色狠狠色综合系列 | 欧美成狂野欧美在线观看 | 兽皇videos日本另类 | 日本卡一卡2卡3卡4精品卡无人区 | 亚洲女同一区二区 | 果冻传媒 天美 麻豆 | 女班长的放荡日记高h | 日本午夜影院 | 操碰91 | futa巨大好爽好长 | 久久久GOGO无码啪啪艺术 | 亚洲老头与老太hd | 高h辣h双处全是肉军婚 | 99热免费在线 | 臀控福利大臀的网站 | jk制服蕾丝超短裙流白浆 | 国内精品久久久久久久久 | 女教师巨大乳孔中文字幕免费 | 色国产视频 | 双性受合不垅腿攻np | 不知火舞被c视频在线播放 不卡一区二区三区卡 | 办公室恋情在线 | 亚洲日本中文字幕天天更新 | 北岛玲在线视频 | 精品综合久久久久久8888 | 欧美肥胖bb| 国产精品一区三区 | 国产haodiaose最新 | 国产99在线| 娇妻被老外疯狂调教 |