千家信息网

SpringBoot多数据源配置的过程是什么

发表于:2024-10-14 作者:千家信息网编辑
千家信息网最后更新 2024年10月14日,本篇内容主要讲解"SpringBoot多数据源配置的过程是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"SpringBoot多数据源配置的过程是什么"
千家信息网最后更新 2024年10月14日SpringBoot多数据源配置的过程是什么

本篇内容主要讲解"SpringBoot多数据源配置的过程是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"SpringBoot多数据源配置的过程是什么"吧!

前言

多数据源的核心就是向 IOC 容器注入 AbstractRoutingDataSource 和如何切换数据源。注入的方式可以是注册 BeanDefinition 或者是构建好的 Bean,切换数据源的方式可以是方法参数或者是注解切换(其他的没想象出来),具体由需求决定。

我的需求是统计多个库的数据,将结果写入另一个数据库,统计的数据库数量是不定的,无法通过 @Bean 直接注入,又是统计任务,DAO 层注解切换无法满足,因此选择注册(AbstractRoutingDataSource 的)BeanDefinition 和方法参数切换来实现。下面以统计统计中日韩用户到结果库为例。

配置文件

master 为结果库,其他为被统计的数据库(china、japan 可以用枚举唯一标识,当然也可以用 String):

dynamic:  dataSources:    master:      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://localhost:3306/result?useUnicode=true&characterEncoding=utf8xxxxxxxx      username: root      password: 123456    china:      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://localhost:3306/china?useUnicode=true&characterEncoding=utf8xxxxxxxx      username: root      password: 123456    japan:      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://localhost:3306/japan?useUnicode=true&characterEncoding=utf8xxxxxxxx      username: root      password: 123456    korea:      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://localhost:3306/korea?useUnicode=true&characterEncoding=utf8xxxxxxxx      username: root      password: 123456

对应的配置类:

package com.statistics.dynamicds.core.config;import com.statistics.dynamicds.core.Country;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;import java.util.Map;import static com.statistics.dynamicds.core.config.MultiDataSourceProperties.PREFIX;@Data@Configuration@ConfigurationProperties(prefix = PREFIX)public class MultiDataSourceProperties {  public static final String PREFIX = "dynamic";  private Map dataSources;  @Data  public static class DataSourceProperties {    private String driverClassName;    private String url;    private String username;    private String password;  }}package com.statistics.dynamicds.core;public enum Country {  MASTER("master", 0),  CHINA("china", 86),  JAPAN("japan", 81),  KOREA("korea", 82),  // 其他国家省略  private final String name;  private final int id;  Country(String name, int id) {    this.name = name;    this.id = id;  }  public int getId() {    return id;  }  public String getName() {    return name;  }}

依赖

ORM 用的 JPA,SpringBoot 版本为 2.3.7.RELEASE,通过 Lombok 简化 GetSet。

    org.springframework.boot    spring-boot-starter-data-jpa     org.projectlombok     lombok     1.18.22     provided

构建 AbstractRoutingDataSource

Spring 的动态数据源需要注入 AbstractRoutingDataSource,因为配置文件中被统计数据源不是固定的,所以不能通过 @Bean 注解注入,需要手动构建。

要在启动类加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class)。

要在启动类加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class)。

要在启动类加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class),重要的事情写三行。

package com.statistics.dynamicds.autoconfig;import com.statistics.dynamicds.core.DynamicDataSourceRouter;import com.statistics.dynamicds.core.Country;import com.statistics.dynamicds.core.config.MultiDataSourceProperties;import com.zaxxer.hikari.HikariDataSource;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.boot.context.properties.bind.Binder;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 javax.annotation.Nonnull;import java.util.Map;import java.util.stream.Collectors;import static com.statistics.dynamicds.core.config.MultiDataSourceProperties.PREFIX;public class MultiDataSourceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {  public static final String DATASOURCE_BEANNAME = "dynamicDataSourceRouter";  private Environment environment;  @Override  public void registerBeanDefinitions(@Nonnull AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    MultiDataSourceProperties multiDataSourceProperties = Binder.get(environment)            .bind(PREFIX, MultiDataSourceProperties.class)            .orElseThrow(() -> new RuntimeException("no found dynamicds config"));    final HikariDataSource[] defaultTargetDataSource = {null};    Map targetDataSources = multiDataSourceProperties.getDataSources().entrySet().stream()            .collect(Collectors.toMap(                    Map.Entry::getKey,                    entry -> {              MultiDataSourceProperties.DataSourceProperties dataSourceProperties = entry.getValue();              HikariDataSource dataSource = DataSourceBuilder.create()                      .type(HikariDataSource.class)                      .driverClassName(dataSourceProperties.getDriverClassName())                      .url(dataSourceProperties.getUrl())                      .username(dataSourceProperties.getUsername())                      .password(dataSourceProperties.getPassword())                      .build();              dataSource.setPoolName("HikariPool-" + entry.getKey());              if (Country.MASTER == entry.getKey()) {                defaultTargetDataSource[0] = dataSource;              }              return dataSource;            }));    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DynamicDataSourceRouter.class)            .addConstructorArgValue(defaultTargetDataSource[0])            .addConstructorArgValue(targetDataSources)            .getBeanDefinition();    registry.registerBeanDefinition(DATASOURCE_BEANNAME, beanDefinition);  }  @Override  public void setEnvironment(@Nonnull Environment environment) {    this.environment = environment;  }}

上面代码中 MultiDataSourceProperties 不是由 @Resource 或者 @Autowired 获取的是因为 ImportBeanDefinitionRegistrar 执行的很早,此时 @ConfigurationProperties 的配置参数类还没有注入,因此要手动获取(加 @ConfigurationProperties 注解是为了使 IOC 容器中其他 Bean 能获取配置的 Country,以此来切换数据源)。

下面是 AbstractRoutingDataSource 的实现类 DynamicDataSourceRouter:

package com.statistics.dynamicds.core;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import java.util.Map;public class DynamicDataSourceRouter extends AbstractRoutingDataSource {  public DynamicDataSourceRouter(Object defaultTargetDataSource, Map targetDataSources) {    this.setDefaultTargetDataSource(defaultTargetDataSource);    this.setTargetDataSources(targetDataSources);  }  @Override  protected Object determineCurrentLookupKey() {    return DataSourceContextHolder.getLookupKey();  }}

数据源切换

数据源的切换由 DataSourceContextHolder 和切面 DynamicDataSourceAspect 控制:

package com.statistics.dynamicds.core;public class DataSourceContextHolder {  private static final ThreadLocal HOLDER = ThreadLocal.withInitial(() -> Country.MASTER);  public static void setLookupKey(Country lookUpKey) {    HOLDER.set(lookUpKey);  }  public static Country getLookupKey() {    return HOLDER.get();  }  public static void clear() {    HOLDER.remove();  }}package com.statistics.dynamicds.core;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class DynamicDataSourceAspect {  @Pointcut("execution(* com.statistics.dao..*.*(..))")  void aspect() {  }  @Around("aspect()")  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {    for (Object arg : joinPoint.getArgs()) {      if (arg instanceof Country) {        DataSourceContextHolder.setLookupKey((Country) arg);        break;      }    }    try {      return joinPoint.proceed();    }finally {      DataSourceContextHolder.clear();    }  }}

到此,相信大家对"SpringBoot多数据源配置的过程是什么"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

数据 数据源 配置 切换 统计 注解 过程 参数 数据库 方法 结果 内容 容器 手动 文件 方式 需求 学习 实用 更深 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 揭东手机软件开发 数据库安全的管理软件 育碧服务器在哪看状态 桥梁防碰撞预警软件开发 梦幻西游各服务器人数 青少年网络安全课第五课 百兆网线千兆网口服务器能用吗 梦幻怎么查询转过哪个服务器 数据库显示附加错误怎么办 软件开发首选桌面指纹仪生产厂家 腾讯贵州数据中心有多少个服务器 如何检查本地数据库端口通不通 连接运营商服务器时出错 斯普林格数据库官网 广州市一呼百应网络技术 流放者柯南连服务器报错 在网络安全方面的看法及做法 顺义区管理软件开发口碑推荐 台湾服务器游戏云空间 数据库链表查找 法度互联网科技有限公司 全国网络安全和信息化安全会议 沂水软件开发有限公司 抚州梦巢互联网科技有限公司 前端数据库对比 绿盟科技网络安全产品怎么样 泗阳威力网络技术参考价格 魔兽世界什么服务器稳定 网络安全全覆盖 嘉兴ios手机软件开发
0