目录

一、导入pom依赖

二、yml配置文件

三、数据源枚举类 

四、Spring工具类

五、配置类

5.1、数据源切换处理类

5.2、动态数据源路由类

5.3、Druid配置属性

5.4、多数据源核心配置类 

六、自定义多数据源切换注解

七、动态数据源的切面类 

八、项目完整截图

九、使用方法

十、Gitee源码 

十一、总结


一、导入pom依赖

<dependencies>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Lombok驱动依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- MySQL驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>

        <!--Mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!-- 阿里数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>

        <!--aop依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
    </dependencies>

二、yml配置文件

# Mybatis配置
mybatis:
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapper-locations: classpath:mapper/*/*.xml

# 数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username:
        password: 
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
        enabled: true
        url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username:
        password:
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置连接超时时间
      connectTimeout: 30000
      # 配置网络超时时间
      socketTimeout: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 设置白名单,不填则允许所有访问
        allow:
        url-pattern: /druid/*
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

三、数据源枚举类 

public enum DataSourceType
{
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE
}

四、Spring工具类

主要作用是提供通过名字获取Bean实例的静态方法。

1、实现BeanFactoryPostProcessor和ApplicationContextAware接口,在Spring容器初始化时,将ConfigurableListableBeanFactory和ApplicationContext的实例保存在静态变量中。

@Component标记此工具类交给Spring容器托管。

@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware{

    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

}

2、postProcessBeanFactory方法会在Bean定义加载完成但实例化之前执行,这时保存BeanFactory实例。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
    SpringUtils.beanFactory = beanFactory;
}

3、setApplicationContext会在上下文准备完成后执行,这时保存ApplicationContext实例。

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
    SpringUtils.applicationContext = applicationContext;
}

4、 提供getBean方法,根据名字从静态的BeanFactory中获取Bean实例。

@SuppressWarnings("unchecked")表示去抑制未检查的转型、参数化变量相关的警告。

@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
    return (T) beanFactory.getBean(name);
}

完整代码: 

package com.example.multiple.utils;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

}

五、配置类

5.1、数据源切换处理类

1、定义了一个ThreadLocal类型的CONTEXT_HOLDER,它将为每个线程提供一个独立的副本storage。

private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

2、setDataSourceType方法用于设置当前线程要使用的数据源类型,会把类型存入CONTEXT_HOLDER这个ThreadLocal中。

public static void setDataSourceType(String dsType)
{
    log.info("切换到{}数据源", dsType);
    CONTEXT_HOLDER.set(dsType);
}

3、getDataSourceType用于获取当前线程所使用的数据源类型,是从CONTEXT_HOLDER这个ThreadLocal中获取。

public static String getDataSourceType()
{
    return CONTEXT_HOLDER.get();
}

4、clearDataSourceType用于清空当前线程的数据源类型信息。

public static void clearDataSourceType()
{
    CONTEXT_HOLDER.remove();
}

这样使用ThreadLocal就能实现一个线程内部共享这个数据源类型变量,并且每个线程的变量都是独立的。 

完整代码:

package com.example.multiple.config.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据源切换处理
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

5.2、动态数据源路由类

1、这个DynamicDataSource类继承了AbstractRoutingDataSource,实现了一个动态切换数据源的路由Datasource。

public class DynamicDataSource extends AbstractRoutingDataSource{

}

2、构造方法中调用父类的方法设置默认数据源和所有目标数据源Map。

public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
    super.setDefaultTargetDataSource(defaultTargetDataSource);
    super.setTargetDataSources(targetDataSources);
    super.afterPropertiesSet();
}

3、实现了determineCurrentLookupKey方法,在此方法中,通过DynamicDataSourceContextHolder工具类获取当前线程上的数据源类型,然后将当前Lookup Key设置为这个数据源类型。

@Override
protected Object determineCurrentLookupKey()
{
    return DynamicDataSourceContextHolder.getDataSourceType();
}

最后,AbstractRoutingDataSource会根据这个Lookup Key,在目标数据源Map中查找对应的DataSource,作为获取连接的源。这样通过determineCurrentLookupKey的实现,动态返回当前线程上的DataSource类型。配合DynamicDataSourceContextHolder来切换设置线程DataSource类型。就能根据线程运行时的数据源类型,动态切换到不同的数据源上获取连接。实现了根据当前运行情况动态切换多个数据源的功能。

完整代码:

package com.example.multiple.config.datasource;

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

import javax.sql.DataSource;
import java.util.Map;

/**
 * 动态数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

5.3、Druid配置属性

从配置中加载属性,并设置到DruidDataSource中,创建一个可用的DataSource实例,代码上都有注释,这边就不多做讲解了。

完整代码:

package com.example.multiple.config.properties;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * druid 配置属性
 *
 */
@Configuration
public class DruidProperties
{
    @Value("${spring.datasource.druid.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.druid.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.druid.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.druid.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.druid.connectTimeout}")
    private int connectTimeout;

    @Value("${spring.datasource.druid.socketTimeout}")
    private int socketTimeout;

    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
    private int maxEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.druid.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.druid.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.druid.testOnReturn}")
    private boolean testOnReturn;

    public DruidDataSource dataSource(DruidDataSource datasource)
    {
        /** 配置初始化大小、最小、最大 */
        datasource.setInitialSize(initialSize);
        datasource.setMaxActive(maxActive);
        datasource.setMinIdle(minIdle);

        /** 配置获取连接等待超时的时间 */
        datasource.setMaxWait(maxWait);

        /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */
        datasource.setConnectTimeout(connectTimeout);

        /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */
        datasource.setSocketTimeout(socketTimeout);

        /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

        /**
         * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
         */
        datasource.setValidationQuery(validationQuery);
        /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
        datasource.setTestWhileIdle(testWhileIdle);
        /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
        datasource.setTestOnBorrow(testOnBorrow);
        /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
        datasource.setTestOnReturn(testOnReturn);
        return datasource;
    }
}

5.4、多数据源核心配置类 

 1、使用@Configuration标记此类为核心配置类。

@Configuration
public class DruidConfig{
}

2、注册一个创建master主数据源的Bean

第一步、@ConfigurationProperties注解加载名称为“spring.datasource.druid.master”的属性配置。

第二步、通过DruidDataSourceBuilder创建一个DruidDataSource实例。

第三步、将DruidDataSource实例传入DruidProperties的dataSource方法中。

第四步、DruidProperties会根据加载的属性配置,设置DruidDataSource的各种属性,如最大连接数,最小连接数等。

第五步、dataSource方法会返回设置好属性的DruidDataSource实例。

最后这个配置好的DruidDataSource将作为masterDataSource Bean的实例,所以它实现了使用Spring Boot的属性配置方式,加载druid.master的配置,并设置到DruidDataSource中,创建一个可用的master数据源Bean。 

@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
    DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
    return druidProperties.dataSource(dataSource);
}

3、注册一个创建salve从数据源的Bean,整体步骤和上面类似,不多做阐述。

@ConditionalOnProperty注解的作用是,根据给定的条件来决定这个Bean是否创建。只有当配置了spring.datasource.druid.slave.enabled=true时,这个slaveDataSource的Bean才会被创建。 

@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
    DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
    return druidProperties.dataSource(dataSource);
}

4、设置数据源的方法setDataSource

第一步、方法接收一个Map对象targetDataSources,数据源名称sourceName和数据源Bean名称beanName作为参数。

第二步、从Spring容器中通过SpringUtils工具类获取beanName对应的DataSource Bean实例。

最后、将得到的DataSource实例根据sourceNamekey,存入targetDataSources这个Map中。 

public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

这样通过beanName加载DataSource,并使用自定义的sourceName作为key存储到targetDataSources中。目标是构建一个自定义名称与数据源实例的映射关系,存在targetDataSources这个容器中。这可以实现多数据源的配置管理,通过不同的sourceName获取对应的数据源实例。 

5.5、实现动态数据源的配置

第一步、创建一个Map用于存放目标数据源,并将名为masterDataSource的Bean作为主数据源放入Map。

第二步、调用setDataSource方法将名为slaveDataSource的Bean放入Map,Key设置为SLAVE。

最后、使用主数据源实例和数据源Map创建DynamicDataSource实例,并设置为Primary,即默认的数据源。

@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
    Map<Object, Object> targetDataSources = new HashMap<>();
    targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
    setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
    return new DynamicDataSource(masterDataSource, targetDataSources);
}

完整代码:

package com.example.multiple.config;

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;

import com.example.multiple.config.datasource.DynamicDataSource;
import com.example.multiple.enums.DataSourceType;
import com.example.multiple.config.properties.DruidProperties;
import com.example.multiple.utils.SpringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

/**
 * druid 配置多数据源
 */
@Configuration
public class DruidConfig
{
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName 数据源名称
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

}

六、自定义多数据源切换注解

1、@Target和@Retention表示该注解可以用于方法和类上,并且可以保留到运行时。

2、@Documented表示该注解会包含在javadoc中。

3、@Inherited表示该注解可以被子类继承。

注解只有一个value属性,类型是DataSourceType枚举,默认值为MASTER。

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切换数据源名称
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

七、动态数据源的切面类 

1、@Aspect标记此类为切面类、@Order指定Bean的加载顺序、@Component标记此类交给Spring容器托管。

@Aspect
@Order(1)
@Component
public class DataSourceAspect{
}

2、@Pointcut定义了切点,这里是匹配所有@DataSource注解的方法或类。

@Pointcut("@annotation(com.example.multiple.annotation.DataSource)"
        + "|| @within(com.example.multiple.annotation.DataSource)")
public void dsPointCut()
{
}

3、获取需要切换的数据源

第一步、通过point.getSignature()获取方法签名,并转成MethodSignature类型。

第二步、调用AnnotationUtils的findAnnotation方法,以方法为目标,获取其上的@DataSource注解。

第三步、如果注解不为空,直接返回该注解。

最后、如果方法上没有注解,则以方法所在类为目标再次查找@DataSource注解,并返回。

public DataSource getDataSource(ProceedingJoinPoint point)
{
    MethodSignature signature = (MethodSignature) point.getSignature();
    DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
    if (Objects.nonNull(dataSource))
    {
        return dataSource;
    }
    return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}

4、@Around定义了切点的处理逻辑为环绕增强 

第一步、调用getDataSource方法获取目标方法需要的DataSource注解。

第二步、判断如果注解不为空,则调用DynamicDataSourceContextHolder的setDataSourceType方法,将注解value的值(数据源类型)设置到其中。

第三步、调用ProceedingJoinPoint的proceed方法,执行目标方法。

最后、在finally中,调用DynamicDataSourceContextHolder的clearDataSourceType方法,清空线程本地的DataSourceType。

 @Around("dsPointCut()")
 public Object around(ProceedingJoinPoint point) throws Throwable
 {
     DataSource dataSource = getDataSource(point);
     if (dataSource != null)
     {
         DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
     }
     try
     {
         return point.proceed();
     }
     finally
     {
         // 销毁数据源 在执行方法之后
         DynamicDataSourceContextHolder.clearDataSourceType();
     }
 }

完整代码:

package com.example.multiple.aspectj;

import com.example.multiple.annotation.DataSource;
import com.example.multiple.config.datasource.DynamicDataSourceContextHolder;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 多数据源处理
 */
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.example.multiple.annotation.DataSource)"
            + "|| @within(com.example.multiple.annotation.DataSource)")
    public void dsPointCut()
    {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        DataSource dataSource = getDataSource(point);

        if (dataSource != null)
        {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }

        try
        {
            return point.proceed();
        }
        finally
        {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource))
        {
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

八、项目完整截图

以上就把多数据源的核心配置讲解完毕了,剩下的就是一些常规整合MyBatis的操作了,我都放在码云上了,这边就不过多写了,完整的项目建包就这样。

【万字长文】SpringBoot整合MyBatis搭建MySQL多数据源完整教程(提供Gitee源码)-LMLPHP

九、使用方法

在Mapper层或者Service层上使用@DataSource自定义注解切换到指定数据源即可。

@Mapper
@DataSource(DataSourceType.SLAVE)
public interface SlaveMapper {

    public List<Logger> select();

}

运行结果如下: 

【万字长文】SpringBoot整合MyBatis搭建MySQL多数据源完整教程(提供Gitee源码)-LMLPHP

十、Gitee源码 

在yml文件配置好自己的主从数据源一键启动项目即可

项目地址:SpringBoot整合MyBatis搭建MySQL多数据源

十一、总结

以上就是我对于SpringBoot如何整合多数据源的技术分析,也是比较传统化的方式,比较复杂,如有问题欢迎评论区讨论!

08-04 15:46