动态数据源切换
1.创建一个springboot项目
2.添加maven依赖
org.springframework.boot spring-boot-starter-aop mysql mysql-connector-java runtime com.alibaba druid-spring-boot-starter 1.1.18 org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-freemarker org.projectlombok lombok com.baomidou mybatis-plus-boot-starter 3.1.0
3.配置application.yml
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdruid:master:jdbc-url: jdbc:mysql://localhost:3306/act_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&nullCatalogMeansCurrent=trueusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driversalve:jdbc-url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&nullCatalogMeansCurrent=trueusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
4.在配置类上启用AOP注解
@EnableAspectJAutoProxy
5、定义一个注解类
/*** @author wts* @date 2022/4/11* @description 自定义多数据源切换注解* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface DataSource {/*** 切换的数据源类型*/String value() default DataSourceName.DB_MerchantCenter;
}
6.定义一个枚举类
/*** @author wts* @date 2022/4/11* @description 数据源*/
public enum DataSourceType {/*** 主库*/MASTER,/*** 从库*/SALVE
}
7.定义一个动态数据源处理器
/*** @author wts* @date 2022/4/11* @description 数据源切换处理*/
public class DynamicDataSourceContextHolder {/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源的变量*/public static void setDataSourceType(String dsType) {CONTEXT_HOLDER.set(dsType);}/*** 获得数据源的变量*/public static String getDataSourceType() {return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void clearDataSourceType() {CONTEXT_HOLDER.remove();}
}
8.定义一个动态数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;/*** @author wts* @date 2022/4/11* @description 动态数据源*/
public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultTargetDataSource, Map
9.定义一个数据源配置类
import com.zcy.common.datasource.DataSourceType;
import com.zcy.common.datasource.DynamicDataSource;
import com.zcy.utils.SpringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** @author wts* @date 2021/4/11* @description 数据源配置类*/
@Configuration
public class DataSourceConfig {@Bean(name = "masterDataSource")@ConfigurationProperties(prefix = "spring.datasource.druid.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "salveDataSource")@ConfigurationProperties(prefix = "spring.datasource.druid.salve")public DataSource salveDataSource() {return DataSourceBuilder.create().build();}@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource() {Map
10.定义一个AOP切面类
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;/*** @author wts* @date 2021/4/11* @description 多数据源处理*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect {protected Logger logger = LoggerFactory.getLogger(getClass());@Pointcut("@annotation(com.zcy.common.datasource.DataSource)" + "|| @within(com.zcy.common.datasource.DataSource)")public void dsPointCut() {}@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {DataSource dataSource = getDataSource(point);if (null != dataSource) {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);}
}
【补充】:AbstractRoutingDataSource分析
抽象类AbstractRoutingDataSource,通过扩展这个类实现根据不同的请求切换数据源。
AbstractRoutingDataSource继承AbstractDataSource,如果声明一个类DynamicDataSource继承AbstractRoutingDataSource后,DynamicDataSource本身就相当于一种数据源。所以AbstractRoutingDataSource必然有getConnection()方法获取数据库连接。如下:
@Override
public Connection getConnection() throws SQLException {return determineTargetDataSource().getConnection();
}@Override
public Connection getConnection(String username, String password) throws SQLException {return determineTargetDataSource().getConnection(username, password);
}
而AbstractRoutingDataSource的getConnection()方法里实际是调用determineTargetDataSource()返回的数据源的getConnection()方法。接着看determineTargetDataSource()方法:
protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = determineCurrentLookupKey();DataSource dataSource = this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");}return dataSource;
}
大致流程为,通过determineCurrentLookupKey()方法获取一个key,通过key从resolvedDataSources中获取数据源DataSource对象。determineCurrentLookupKey()是个抽象方法,需要继承AbstractRoutingDataSource的类实现;而resolvedDataSources是一个Map,里面应该保存当前所有可切换的数据源。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
