[Spring Boot] Spring Boot 多数据源动态切换[自定义注解&AOP]

Spring 同时被 2 个专栏收录
33 篇文章 0 订阅
24 篇文章 0 订阅

[Spring Boot] Spring Boot 多数据源动态切换[自定义注解&AOP]

手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。

平台地址
CSDNhttps://blog.csdn.net/sinat_28690417
简书https://www.jianshu.com/u/3032cc862300
个人博客http://xiazhaoyang.tech/

开发环境描述

------------------------------------------------------------
Gradle 4.7
------------------------------------------------------------

Build time:   2018-04-18 09:09:12 UTC
Revision:     b9a962bf70638332300e7f810689cb2febbd4a6c

Groovy:       2.4.12
Ant:          Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM:          1.8.0_171 (Oracle Corporation 25.171-b11)
OS:           Mac OS X 10.13.5 x86_64

依赖描述

  • spring-boot-starter-web:2.1.0.RELEASE
  • mybatis-spring-boot-starter:1.3.2
  • aspectjrt:1.9.2
  • aspectjweaver:1.9.2
  • mysql-connector-java:8.0.13

基础框架准备

  • 准备两个数据源(mysql中schema和database是一个意思)并生成对应MapperModel

    CREATE SCHEMA db_capsule;

    CREATE SCHEMA db_flowable;

    分别在这两个数据库中创建一张表:db_capsule.tb_common_user_infodb_flowable.tb_common_account_info

  create table tb_common_user_info
  (
    user_id     bigint auto_increment
    comment '人员ID'
      primary key,
    age         int                                not null
    comment '年龄',
    name        varchar(128)                       not null
    comment '姓名',
    email       varchar(128)                       not null
    comment '邮箱',
    remark      varchar(1024)                      null
    comment '备注',
    is_delete   int default '0'                    null
    comment '表明数据是否已删除 0-未删除,1-已删除',
    create_time datetime default CURRENT_TIMESTAMP not null
    comment '创建时间'
  )
    comment '通用人员信息表';
  
  create table tb_common_account_info
  (
    account_id   bigint auto_increment
    comment '账号ID'
      primary key,
    account_name varchar(128)                       not null
    comment '登录名',
    user_id      bigint                             null
    comment '关联用户ID',
    remark       varchar(1024)                      null
    comment '备注',
    is_delete    int default '0'                    null
    comment '表明数据是否已删除 0-未删除,1-已删除',
    create_time  datetime default CURRENT_TIMESTAMP not null
    comment '创建时间'
  )
    comment '账号信息表';
  

生成基础Mapper(代码生成插件即可)

在这里插入图片描述

在这里插入图片描述

  • yaml中配置默认数据源和自定义数据源
spring:
  mvc:
   static-path-pattern: /**
  resources:
    static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/static/flowable-modeler
  # 默认主数据源
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    username: xx
    password: xxxx
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://x.x.x.x:3306/db_flowable?useUnicode=true&characterEncoding=utf-8
  jpa:
    hibernate:
      ddl-auto: update
    database: MYSQL

# 自定义数据源
custom:
  datasource:
    - key: capsule
      type: com.alibaba.druid.pool.DruidDataSource
      username: xxx
      password: xxxx
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://x.x.x.x:3306/db_capsule?useUnicode=true&characterEncoding=utf-8
  • 启动类开启AOP
package com.example;

import com.example.common.config.database.DynamicDataSourceRegister;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * <p>
 *  - SpringBootApplication 启动类
 *  - ComponentScan 实例扫描
 *  - MapperScan Mybatis Dao 扫描
 *  - EnableTransactionManagement 开启事务
 *  - Import 启动前注入实例,动态切换数据源
 * </p>
 *
 * @author xiazhaoyang
 * @version v1.0.0
 * @date 2019/3/14 23:08
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019/3/14
 * @modify reason: {方法名}:{原因}
 * ...
 */
@SpringBootApplication
@ComponentScan(basePackages = {"com.example"})
@Import({DynamicDataSourceRegister.class})
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class CapsuleFlowableApplication {

    public static void main(String[] args) {
        SpringApplication.run(CapsuleFlowableApplication.class, args);
    }
}

  • 构建Service层测试代码

    AccountInfoServiceImpl

package com.example.service.common.impl;
import com.example.common.base.BaseServiceImpl;
import com.example.common.base.RootMapper;
import com.example.common.config.database.TargetDataSource;
import com.example.core.common.dao.AccountInfoMapper;
import com.example.core.common.model.AccountInfo;
import com.example.core.common.model.AccountInfoExample;
import com.example.service.common.AccountInfoService;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0.0
 * @date 2019/03/24
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人}  2019/03/24
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Slf4j
@Service
public class AccountInfoServiceImpl extends BaseServiceImpl<AccountInfo,AccountInfoExample> implements AccountInfoService {

    @Resource
    private AccountInfoMapper accountInfoMapper;


    @Override
    public RootMapper<AccountInfo, AccountInfoExample> getMapper() {
        return accountInfoMapper;
    }


    @Override
    @TargetDataSource(name="dataSource")//此处为切换数据源的注解
    public List<AccountInfo> selectList() {
        return selectByExample(new AccountInfoExample());
    }
}

UserInfoServiceImpl

package com.example.service.common.impl;
import com.example.common.config.database.TargetDataSource;
import com.example.core.common.dao.UserInfoMapper;
import com.example.core.common.model.UserInfo;
import com.example.core.common.model.UserInfoExample;
import com.example.service.common.UserInfoService;
import com.example.common.base.BaseServiceImpl;
import com.example.common.base.RootMapper;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0.0
 * @date 2019/03/24
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人}  2019/03/24
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Slf4j
@Service
public class UserInfoServiceImpl extends BaseServiceImpl<UserInfo,UserInfoExample> implements UserInfoService {

    @Resource
    private UserInfoMapper userInfoMapper;

    @Override
    public RootMapper<UserInfo, UserInfoExample> getMapper() {
        return userInfoMapper;
    }


    @Override
    @TargetDataSource(name="capsule")
    public List<UserInfo> selectList() {
        return selectByExample(new UserInfoExample());
    }

    @Override
    @TargetDataSource(name="capsule")//此处为切换数据源的注解
    public int insert(UserInfo userInfo) {
        return super.insert(userInfo);
    }
}

  • 构建Controller层测试代码

AccountInfoController

package com.example.web.controller.common;

import com.example.common.base.BaseController;
import com.example.common.base.ResponseJson;
import com.example.core.common.model.AccountInfo;
import com.example.service.common.AccountInfoService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Date;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0.0
 * @date 2019/03/24
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人}  2019/03/24
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Slf4j
@RestController
@RequestMapping("/common/accountInfo/")
public class AccountInfoController extends BaseController<AccountInfo>{

    @Resource
    AccountInfoService accountInfoService;

    @Override
    @RequestMapping(value="addition", method = RequestMethod.POST)
    public ResponseJson add(@RequestBody AccountInfo accountInfo) {
        accountInfoService.insert(AccountInfo.builder()
                .accountId(1L)
                .accountName("admin")
                .createTime(new Date())
                .userId(1L)
                .remark("ADMIN REMARK")
                .isDelete(0)
                .build());
        return new ResponseJson();
    }

    @Override
    @RequestMapping(value="deletion", method = RequestMethod.POST)
    public ResponseJson delete(@RequestParam String id) {
	    return new ResponseJson();
    }

    @Override
    @RequestMapping(value="update", method = RequestMethod.POST)
    public ResponseJson update(@RequestBody AccountInfo accountInfo) {
	    return new ResponseJson();
    }

    @Override
    @RequestMapping(value="detail", method = RequestMethod.POST)
    public ResponseJson detail(@RequestParam String id) {
        return ResponseJson.OK(accountInfoService.selectList());
    }
}

UserInfoController

package com.example.web.controller.common;
import com.example.core.common.model.AccountInfo;
import com.example.core.common.model.UserInfo;
import com.example.service.common.UserInfoService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import com.example.common.base.BaseController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import com.example.common.base.ResponseJson;

import java.util.Date;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version V1.0.0
 * @date 2019/03/24
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人}  2019/03/24
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Slf4j
@RestController
@RequestMapping("/common/userInfo/")
public class UserInfoController extends BaseController<UserInfo>{

    @Resource
    UserInfoService userInfoService;

    @Override
    @RequestMapping(value="addition", method = RequestMethod.POST)
    public ResponseJson add(@RequestBody UserInfo userInfo) {
        userInfoService.insert(UserInfo.builder()
                .userId(1L)
                .age(11)
                .createTime(new Date())
                .email("xxx@xx.com.cn")
                .name("Yiyuery")
                .remark("xxx")
                .build());
        return new ResponseJson();
    }

    @Override
    @RequestMapping(value="deletion", method = RequestMethod.POST)
    public ResponseJson delete(@RequestParam String id) {
	    return new ResponseJson();
    }

    @Override
    @RequestMapping(value="update", method = RequestMethod.POST)
    public ResponseJson update(@RequestBody UserInfo userInfo) {
	    return new ResponseJson();
    }

    @Override
    @RequestMapping(value="detail", method = RequestMethod.POST)
    public ResponseJson detail(@RequestParam String id) {
        return ResponseJson.OK(userInfoService.selectList());
    }
}

动态数据源注册

  • 注解定义
  /*
   * @ProjectName: 编程学习
   * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
   * @address:     http://xiazhaoyang.tech
   * @date:        2019/3/23 17:05
   * @email:       xiazhaoyang@live.com
   * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
   */
  package com.example.common.config.database;
  
  import java.lang.annotation.*;
  
  /**
   * <p>
   *  在方法上使用,用于指定使用哪个数据源
   * </p>
   *
   * @author xiazhaoyang
   * @version v1.0.0
   * @date 2019/3/23 17:05
   * @modificationHistory=========================逻辑或功能性重大变更记录
   * @modify By: {修改人} 2019/3/23
   * @modify reason: {方法名}:{原因}
   * ...
   */
  @Target({ ElementType.METHOD, ElementType.TYPE })
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  public @interface TargetDataSource {
      String name();
  }
  • DynamicDataSourceRegister数据源实例bean注册
  /*
   * @ProjectName: 编程学习
   * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
   * @address:     http://xiazhaoyang.tech
   * @date:        2019/3/23 17:04
   * @email:       xiazhaoyang@live.com
   * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
   */
  package com.example.common.config.database;
  
  import lombok.extern.slf4j.Slf4j;
  import org.apache.commons.lang3.StringUtils;
  import org.springframework.beans.MutablePropertyValues;
  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  import org.springframework.beans.factory.support.GenericBeanDefinition;
  import org.springframework.boot.context.properties.bind.Bindable;
  import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
  import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
  import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
  import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
  import org.springframework.boot.jdbc.DataSourceBuilder;
  import org.springframework.context.EnvironmentAware;
  import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
  import org.springframework.core.env.Environment;
  import org.springframework.core.type.AnnotationMetadata;
  import org.springframework.boot.context.properties.bind.Binder;
  
  
  import javax.sql.DataSource;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import static com.example.common.util.CapsuleStringUtil.requireNotSatisfy;
  
  /**
   * <p>
   *
   * </p>
   *
   * @author xiazhaoyang
   * @version v1.0.0
   * @date 2019/3/23 17:04
   * @modificationHistory=========================逻辑或功能性重大变更记录
   * @modify By: {修改人} 2019/3/23
   * @modify reason: {方法名}:{原因}
   * ...
   */
  @Slf4j
  public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
  
      /**
       * 参数绑定工具
       */
      private Binder binder;
      /**
       * 如配置文件中未指定数据源类型,使用该默认值
       */
      private static final Object DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
  
      /**
       * 默认数据源
       */
      private DataSource defaultDataSource;
      /**
       * 自定义数据源
       */
      private Map<String, DataSource> customDataSources = new HashMap<>();
      /**
       * 数据源参数配置别名
       */
      private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); //别名
      /**
       * 配置上下文(也可以理解为配置文件的获取工具)
       */
      private Environment env;
  
  
      static {
          //由于部分数据源配置不同,所以在此处添加别名,避免切换数据源出现某些参数无法注入的情况
          aliases.addAliases("url", "jdbc-url");
          aliases.addAliases("username", "user");
      }
  
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
          Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
          // 将主数据源添加到更多数据源中
          targetDataSources.put("dataSource", defaultDataSource);
          DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
          // 添加更多数据源
          targetDataSources.putAll(customDataSources);
          DynamicDataSourceContextHolder.dataSourceIds.addAll(customDataSources.keySet());
          // 创建DynamicDataSource
          GenericBeanDefinition define = new GenericBeanDefinition(); //bean定义类
          define.setBeanClass(DynamicDataSource.class); //设置bean的类型,此处DynamicDataSource是继承AbstractRoutingDataSource的实现类
          MutablePropertyValues mpv = define.getPropertyValues(); //需要注入的参数,类似spring配置文件中的<property/>
          mpv.add("defaultTargetDataSource", defaultDataSource); //添加默认数据源,避免key不存在的情况没有数据源可用
          mpv.add("targetDataSources", targetDataSources); //添加其他数据源
          registry.registerBeanDefinition("datasource", define); //将该bean注册为datasource,不使用spring-boot自动生成的datasource
          log.info("Dynamic DataSource Registry");
      }
  
      /**
       * 创建DataSource
       *
       * @return
       * @author SHANHY
       * @create 2016年1月24日
       */
      private DataSource buildDataSource(Map<String, Object> dsMap) {
          try {
              Object type = dsMap.get("type");
              if (type == null)
                  type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
  
              Class<? extends DataSource> dataSourceType;
              dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
              String driverClassName = dsMap.get("driver-class-name").toString();
              String url = dsMap.get("url").toString();
              String username = dsMap.get("username").toString();
              String password = dsMap.get("password").toString();
              DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
                      .username(username).password(password).type(dataSourceType);
              return factory.build();
          } catch (Throwable e) {
              log.error("buildDataSource failed!",e);
          }
          return null;
      }
  
      /**
       * 加载多数据源配置
       */
      @Override
      public void setEnvironment(Environment environment) {
          this.env = environment;
          binder = Binder.get(env); //绑定配置器
          initDefaultDataSource();
          initCustomDataSources();
      }
  
      /**
       * 初始化主数据源
       *
       * @author SHANHY
       * @create 2016年1月24日
       */
      private void initDefaultDataSource() {
          //读取数据源参数配置
          Map props = binder.bind("spring.datasource", Map.class).get();
          Map<String, Object> dsMap = new HashMap<>();
          dsMap.put("type", props.get("type"));
          dsMap.put("driver-class-name", props.get("driver-class-name"));
          dsMap.put("url", props.get("url"));
          dsMap.put("username", props.get("username"));
          dsMap.put("password", props.get("password"));
          defaultDataSource = buildDataSource(dsMap);
          dataBinder(defaultDataSource, props);
      }
  
      /**
       * 初始化更多数据源
       *
       * @author SHANHY
       * @create 2016年1月24日
       */
      private void initCustomDataSources() {
          // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
          List<Map> configs = binder.bind("custom.datasource", Bindable.listOf(Map.class)).get();
          String dsPrefix;
          DataSource custom;
          for (Map config : configs) {
              dsPrefix = requireNotSatisfy(p -> StringUtils.isNotEmpty(config.get("key").toString()), config.get("key").toString(), "default");
              custom = buildDataSource(config);
              customDataSources.put(dsPrefix, custom);
              dataBinder(custom, config);
          }
      }
  
      /**
       * 绑定参数,以下三个方法都是参考DataSourceBuilder的bind方法实现的,
       * 目的是尽量保证我们自己添加的数据源构造过程与spring-boot保持一致
       *
       * @param dataSource
       * @param properties
       */
      private void dataBinder(DataSource dataSource, Map properties) {
          ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
          Binder binderEx = new Binder(source.withAliases(aliases));
          binderEx.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(dataSource));  //将参数绑定到对象
      }
  
  }
  • DynamicDataSourceContextHolder缓存当前线程上下文中数据源标识ID
/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2019/3/23 16:57
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.example.common.config.database;

import com.example.common.util.CapsuleStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

import static com.example.common.util.CapsuleStringUtil.requireNotSatisfyThrowException;

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version v1.0.0
 * @date 2019/3/23 16:57
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019/3/23
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Slf4j
class DynamicDataSourceContextHolder {

    //保存当前线程的数据源对应的key
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    static List<String> dataSourceIds = new ArrayList<>();

    static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    static String getDataSourceType() {
        try {
            return requireNotSatisfyThrowException(p->StringUtils.isNotBlank(contextHolder.get()),contextHolder.get(),"can not found datasource by key: '%s',this session may use default datasource","");
        } catch (NullPointerException e) {
            contextHolder.set("dataSource");
            log.error(e.getMessage());
            //如果动态数据源获取为空,返回默认数据源
            return contextHolder.get();
        }
    }

    static void clearDataSourceType() {
        contextHolder.remove();
    }

    /**
     *
     * 判断指定DataSrouce当前是否存在
     * @return
     * @author xiazhaoyang
     * @date 2019/3/24 17:52
     * @modify by: {修改人} 2019/3/24 17:52
     * @modify by reason:
     * @since 1.0.0
     */
    static boolean containsDataSource(String dataSourceId){
        return dataSourceIds.contains(dataSourceId);
    }
}

  • DataSourceDynamicAspectAOP切片解析注解
  /*
   * @ProjectName: 编程学习
   * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
   * @address:     http://xiazhaoyang.tech
   * @date:        2019/3/24 11:03
   * @email:       xiazhaoyang@live.com
   * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
   */
  package com.example.common.config.database;
  
  import org.aspectj.lang.annotation.Aspect;
  import org.springframework.core.annotation.Order;
  import lombok.extern.slf4j.Slf4j;
  import org.aspectj.lang.annotation.*;
  import org.springframework.stereotype.Component;
  import org.aspectj.lang.JoinPoint;
  
  /**
   * <p>
   * 声明数据源切面
   * </p>
   *
   * @author xiazhaoyang
   * @version v1.0.0
   * @date 2019/3/24 11:03
   * @modificationHistory=========================逻辑或功能性重大变更记录
   * @modify By: {修改人} 2019/3/24
   * @modify reason: {方法名}:{原因}
   * ...
   */
  @Component
  @Aspect
  @Order(-10) //使该切面在事务之前执行
  @Slf4j
  public class DataSourceDynamicAspect {
  
      /**
       * AOP切面拦截注解 TargetDataSource 先从当前线程中取出数据库标识
       * @param point
       * @param ds
       */
      @Before("@annotation(ds)")
      public void changeDataSource(JoinPoint point, TargetDataSource ds) {
          String dsId = ds.name();
          if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
              log.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
          } else {
              log.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());
              DynamicDataSourceContextHolder.setDataSourceType(ds.name());
          }
      }
  
      /**
       * AOP切面拦截注解 TargetDataSource 从当前线程中删除数据库标识
       * @param point
       * @param ds
       */
      @After("@annotation(ds)")
      public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
          log.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
          DynamicDataSourceContextHolder.clearDataSourceType();
      }
  
  
  }
  • DynamicDataSource动态数据源对象定义,用于数据源实例注册到Spring实例工厂后的路由
/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
 * @address:     http://xiazhaoyang.tech
 * @date:        2019/3/23 16:56
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.example.common.config.database;

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

/**
 * <p>
 *
 * </p>
 *
 * @author xiazhaoyang
 * @version v1.0.0
 * @date 2019/3/23 16:56
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019/3/23
 * @modify reason: {方法名}:{原因}
 * ...
 */
public class DynamicDataSource extends AbstractRoutingDataSource {


    /**
     * AbstractRoutingDataSource抽象类实现方法,即获取当前线程数据源的key
     *
     * @return data unique key
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

AbstractRoutingDataSource

Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
calls to one of various target DataSources based on a lookup key. The latter is usually
(but not necessarily) determined through some thread-bound transaction context.
基于查找标识键来调用各种目标数据源之一的路由 {@link #getConnection ()} 的抽象实现{@link javax.sql.DataSource}。后者通常是(但不一定) 通过某些线程绑定事务上下文确定。

效果

  • 账号信息查询

在这里插入图片描述

  • 人员信息查询

    在这里插入图片描述

REFRENCES

更多

扫码关注“架构探险之道”,获取更多源码和文章资源

在这里插入图片描述

知识星球(扫码加入获取源码和文章资源链接)

在这里插入图片描述

  • 2
    点赞
  • 0
    评论
  • 9
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值