# data-source-demo **Repository Path**: feizhaiyou/data-source-demo ## Basic Information - **Project Name**: data-source-demo - **Description**: 该项目是SpringBoot基于Druid连接池,通过AOP实现多数据源的配置与切换,用于代码中链接不同类型的数据库。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-12-24 - **Last Updated**: 2022-04-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: druid, SpringBoot, Oracle, MySQL, aop ## README # 应用场景 单个项目中需要连接多个数据源(同域同类型不同库,同域不同类型,不同域...) # 核心技术 AOP:通过类、方法切入,在执行前获取所需要的数据源。 AbstractRoutingDataSource:JDBC提供,用来配置多个数据源,可以通过指定key获取配置好的数据源。【DataSource抽象实现类】 数据源配置类:配置多个数据源的bean,存放到IOC容器中,并将所有数据源bean设置到AbstractRoutingDataSource的动态数据源中。 SpringBoot启动类:排除数据源自动装配DataSourceAutoConfiguration。 # 配置解析 ## 1.Maven依赖 ~~~xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.8.RELEASE com.sen data-source-demo 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.1 org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java 8.0.21 com.oracle ojdbc8 18.3 org.springframework.boot spring-boot-starter-aop org.aspectj aspectjweaver 1.9.1 com.alibaba druid-spring-boot-starter 1.1.14 ~~~ ## 2.yml配置文件 application.yml ~~~yml logging: level: com.feizhaiyou.demo: debug mybatis: mapper-locations: mapper/*.xml server: port: 10010 spring: profiles: active: druid ~~~ application-druid.yml ~~~yml # 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: master: # mysql8.0驱动配置 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 # 从库数据源 slave: # 是否开启从数据源(如果不是true会使用默认数据源MASTER) enabled: true driverClassName: oracle.jdbc.driver.OracleDriver(驱动类) url: 数据库URL(公司的Orcale数据库不便展示) username: 自己的数据库连接名 password: 自己的数据库密码 slave2: enabled: true driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 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/* # 控制台管理用户名和密码 login-username: 123 login-password: 123 filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true ~~~ 说明:为了方便,单独拉出的yml文件配置数据源 ## 3.自定义注解及多数据源Key 注解用来作为AOP的切点 ```java package com.feizhaiyou.demo.anno; import com.feizhaiyou.demo.enums.DataSourceType; import java.lang.annotation.*; /** * 自定义多数据源切换注解 * * @author Jason */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface DataSource { /** * 切换数据源key 默认master */ public DataSourceType value() default DataSourceType.MASTER; } ``` 枚举类用来作为数据源的key ~~~java package com.feizhaiyou.demo.enums; /** * 数据源key * * @author Jason */ public enum DataSourceType { /** * 主库 */ MASTER, /** * 从库 */ SLAVE, /** * 从库2 */ SLAVE2, } ~~~ ## 4.设置AOP切面 用来获取指定的数据源配置 ~~~java package com.feizhaiyou.demo.aop; import com.feizhaiyou.demo.anno.DataSource; import com.feizhaiyou.demo.holder.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.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 多数据源处理 * * @author Jason */ @Aspect @Order(1) @Component public class DataSourceAspect { // 切点 @Pointcut("@annotation(com.feizhaiyou.demo.anno.DataSource)" + "|| @within(com.feizhaiyou.demo.anno.DataSource)") public void dsPointCut() { } @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { // 获取该切点的DataSource注解 DataSource dataSource = getDataSource(point); if (dataSource != null) { // 设置数据源key 通过该key获取DynamicDataSource中指定的动态数据源 DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); } try { return point.proceed(); } finally { // 销毁数据源 在执行方法之后 清空数据源key 释放该数据源 DynamicDataSourceContextHolder.clearDataSourceType(); } } /** * 获取切点上的DataSource注解{@link DataSource} * @param point 切点 * @return {@link DataSource} */ private DataSource getDataSource(ProceedingJoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); Class targetClass = point.getTarget().getClass(); DataSource targetDataSource = targetClass.getAnnotation(DataSource.class); if (targetDataSource != null) { return targetDataSource; } else { Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); return dataSource; } } } ~~~ ## 5.设置线程使用的数据源key 该线程会使用存储的key来调用对应的动态数据源 ~~~java package com.feizhaiyou.demo.holder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 数据源key处理(存储执行线程所需要的数据源的key) * * @author Jason */ public class DynamicDataSourceContextHolder { private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /** * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 */ private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); /** * 设置线程数据源key */ public static void setDataSourceType(String dsType) { log.info("切换到{}数据源", dsType); CONTEXT_HOLDER.set(dsType); } /** * 获得线程数据源key */ public static String getDataSourceType() { return CONTEXT_HOLDER.get(); } /** * 清空线程数据源key */ public static void clearDataSourceType() { CONTEXT_HOLDER.remove(); } } ~~~ ## 6.动态数据源配置【重点】 实现AbstractRoutingDataSource类的方法,指定默认数据源和动态数据源集合 ~~~java package com.feizhaiyou.demo.config; import com.feizhaiyou.demo.holder.DynamicDataSourceContextHolder; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 动态数据源,配置好后可以通过key获取数据源 * * @author Jason */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源 * 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可 * @return DataSource */ @Override protected DataSource determineTargetDataSource() { return super.determineTargetDataSource(); } /** * 设置需要获取的数据源的key,通过该key获取数据源,该方法直接返回需要获取的数据源的key即可 * targetDataSources找不到该key,则会使用默认数据源 * @return */ @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } /** * 设置动态数据源,设置后可以通过determineCurrentLookupKey方法返回的key动态获取targetDataSources中的数据源 * @param defaultTargetDataSource 默认数据源 * @param targetDataSources 动态数据源集合:key={@link com.feizhaiyou.demo.enums.DataSourceType} value={@link DataSource} */ public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { // 设置默认数据源(MASTER) super.setDefaultTargetDataSource(defaultTargetDataSource); // 设置指定数据源(MASTER SLAVE SLAVE2) super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } } ~~~ ## 7.数据源配置类与Druid属性文件【重点】 用于手动配置数据源bean,加载到IOC容器中,并配置成动态数据源集合。 由于使用了Druid连接池,所以需要添加个属性文件来读取yml文件中的配置。 属性文件: ~~~java package com.feizhaiyou.demo.properties; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; /** * druid 配置属性 * * @author Jason */ @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.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.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; } } ~~~ 数据源配置文件: ~~~java package com.feizhaiyou.demo.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; import com.feizhaiyou.demo.enums.DataSourceType; import com.feizhaiyou.demo.properties.DruidProperties; import com.feizhaiyou.demo.ustils.SpringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.servlet.*; import javax.sql.DataSource; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * druid 配置多数据源 * * @author Jason */ @Configuration public class DruidConfig { private static final Logger log = LoggerFactory.getLogger(DruidConfig.class); // 设置数据源bean @Bean @ConfigurationProperties("spring.datasource.druid.master") public DataSource masterDataSource(DruidProperties druidProperties) { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); return druidProperties.dataSource(dataSource); } // 设置数据源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); } // TODO 设置数据源bean 读取配置 // 设置数据源beanName @Bean("slave2DataSource") // 读取的数据源配置 @ConfigurationProperties("spring.datasource.druid.slave2") // 判断是否读取 enabled: true 读取 @ConditionalOnProperty(prefix = "spring.datasource.druid.slave2", name = "enabled", havingValue = "true") public DataSource slave2DataSource(DruidProperties druidProperties) { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); return druidProperties.dataSource(dataSource); } /** * 设置指定的动态数据源 {@link DynamicDataSource} * * @param masterDataSource * @return */ @Bean(name = "dynamicDataSource") @Primary public DynamicDataSource dataSource(DataSource masterDataSource) { Map targetDataSources = new HashMap<>(); // 添加MUSTER动态数据源 targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); // 添加SLAVE动态数据源 setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); // TODO 设置数据源 参数:动态数据源集合,动态数据源key,对应数据源beanName setDataSource(targetDataSources, DataSourceType.SLAVE2.name(), "slave2DataSource"); // 设置默认数据源和指定的动态数据源 return new DynamicDataSource(masterDataSource, targetDataSources); } /** * 设置数据源 * * @param targetDataSources 动态数据源集合 * @param sourceName 动态数据源key * @param beanName bean名称 */ private void setDataSource(Map targetDataSources, String sourceName, String beanName) { try { // 通过bean名称从spring IOC中获取数据源bean DataSource dataSource = SpringUtils.getBean(beanName); // 添加到动态数据源集合 targetDataSources.put(sourceName, dataSource); } catch (Exception e) { // enabled配置不为true的时候会报该错,即IOC中没有数据源bean // 不会加载到targetDataSources动态数据源集合中,如果注解有使用该数据源的key,则会使用默认的数据源 log.warn("如果配置文件没有启用{}数据源,请忽略!",sourceName); log.error("IOC中没有{},请检查数据源配置类beanName是否正确!",beanName); } } /** * 去除监控页面底部的广告 */ @SuppressWarnings({"rawtypes", "unchecked"}) @Bean @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) { // 获取web监控页面的参数 DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); // 提取common.js的配置路径 String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); final String filePath = "support/http/resources/js/common.js"; // 创建filter进行过滤 Filter filter = new Filter() { @Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); // 重置缓冲区,响应头不会被重置 response.resetBuffer(); // 获取common.js String text = Utils.readFromResource(filePath); // 正则替换banner, 除去底部的广告信息 text = text.replaceAll("
", ""); text = text.replaceAll("powered.*?shrek.wang", ""); response.getWriter().write(text); } @Override public void destroy() { } }; FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(filter); registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; } } ~~~ ## 8.启动类关闭自动配置数据源 数据源需要上面的配置文件手动配置,所以需要排除SpringBoot的数据源自动装配 ~~~java package com.feizhaiyou.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; /** * @author Jason */ // TODO 关闭数据源自动装配 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @MapperScan(basePackages = "com.feizhaiyou.demo.dao") public class DataSourceApplication { public static void main(String[] args) { SpringApplication.run(DataSourceApplication.class, args); System.out.println("=========多数据源切换Demo启动=========="); } } ~~~ ## 9.其他工具类 通过bean名称从IOC中获取对应的bean ~~~java package com.feizhaiyou.demo.ustils; import org.springframework.aop.framework.AopContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; /** * spring工具类 方便在非spring管理环境中获取bean * * @author Jason */ @Component public final class SpringUtils implements BeanFactoryPostProcessor { /** Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws BeansException * */ @SuppressWarnings("unchecked") public static T getBean(String name) throws BeansException { return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws BeansException * */ public static T getBean(Class clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; } /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return beanFactory.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws NoSuchBeanDefinitionException * */ public static Class getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); } /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); } } ~~~ # 添加数据源操作 ## 1.springboot配置文件中追加数据源 application-druid.yml ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164721_48748eb4_9705983.png "image-20211224140939252.png") ~~~yml slave2: enabled: true driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 ~~~ ## 2.设置对应的数据源key DataSourceType.java ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164743_4cb2d407_9705983.png "image-20211224144229647.png") ## 3.数据源配置类手动配置新增数据源bean DruidConfig文件: ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164803_6abcf161_9705983.png "image-20211224143643138.png") ~~~java // TODO 设置数据源bean 读取配置 // 设置数据源beanName @Bean("slave2DataSource") // 读取的数据源配置 @ConfigurationProperties("spring.datasource.druid.slave2") // 判断是否读取 enabled: true 读取 @ConditionalOnProperty(prefix = "spring.datasource.druid.slave2", name = "enabled", havingValue = "true") public DataSource slave2DataSource(DruidProperties druidProperties) { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); return druidProperties.dataSource(dataSource); } ~~~ ## 4.将数据源bean加载到动态数据源集合中 DruidConfig文件:dataSource方法 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164824_e4e294ef_9705983.png "image-20211224145342294.png") ~~~java setDataSource(targetDataSources, DataSourceType.SLAVE2.name(), "slave2DataSource"); ~~~ ## 5.在Service层添加注解使用数据源 该类下面所有的方法都会使用指定key的数据源 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164842_28c7176a_9705983.png "image-20211224145753457.png") ~~~java @Service @DataSource(value = DataSourceType.SLAVE2) public class DataSource3ServiceImpl implements DataSource3Service { @Autowired private DataSource3Mapper dataSource3Mapper; } ~~~ # 测试 ## 数据源展示 master数据源:连接mysql.test库 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164903_d760d16b_9705983.png "image-20211224150211870.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164917_d7fc83e4_9705983.png "image-20211224150321273.png") slave数据源:连接oracle库(公司数据库不便展示) ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164931_d2dc659d_9705983.png "image-20211224150520042.png") slave2数据源:连接mysql.nacos库(懒得建了) ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/164945_2f68a91c_9705983.png "image-20211224150651349.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165001_3faa29e5_9705983.png "image-20211224150714565.png") ## 测试代码 Controller ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165020_07c839b8_9705983.png "image-20211224150806363.png") Service ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165034_d6131445_9705983.png "image-20211224150853795.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165047_eb5a35fb_9705983.png "image-20211224150923497.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165059_7e03341b_9705983.png "image-20211224150954547.png") mapper ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165115_0e75dd31_9705983.png "image-20211224151050170.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165128_9e055225_9705983.png "image-20211224151117163.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165145_fdb1b4f1_9705983.png "image-20211224151143539.png") ## PostMan测试 ### 请求master数据源接口 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165159_96df00a4_9705983.png "image-20211224151447972.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165212_7fddef21_9705983.png "image-20211224151418748.png") ### 请求slave数据源接口 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165224_9b4af2c0_9705983.png "image-20211224151707644.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165235_3fee28af_9705983.png "image-20211224151638749.png") ### 请求slave2数据源接口 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165248_21000026_9705983.png "image-20211224151924602.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165301_288a544c_9705983.png "image-20211224151827853.png") ### 测试关闭slave2数据源 ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165316_c4026def_9705983.png "image-20211224152102317.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165330_3a3b18e9_9705983.png "image-20211224152226962.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165342_69cd28cc_9705983.png "image-20211224152822646.png") ![输入图片说明](https://images.gitee.com/uploads/images/2021/1224/165351_d733c8ad_9705983.png "image-20211224155457867.png") # 使用建议 建议将@Datasource注解加到Service层的实现类上。 建议将操作比较多的数据源设置为默认数据源,即master数据源。 不建议将使用默认(master)数据源的类上添加@Datasource(value = DataSourceType.MASTER)注解。