java springboot 多数据源
yum配置
# 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver druid: # 主库数据源 master: url: jdbc:mysql://120.78.127.53:3306/zipkin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 123456 # 从库数据源 slave: # 从数据源开关/默认关闭 enabled: true url: jdbc:mysql://47.107.168.139:3306/zipkin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 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: /monitor/druid/* filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
DruidConfig
import com.alibaba.druid.filter.Filter; import com.alibaba.druid.filter.stat.StatFilter; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import com.alibaba.druid.support.http.StatViewServlet; import com.table.common.datasource.DynamicDataSource; import com.table.common.enums.DataSourceType; import org.assertj.core.util.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.ServletRegistrationBean; 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; /** * druid 配置多数据源 * * @author ruoyi */ @Configuration public class DruidConfig { @Bean("masterDataSource") @ConfigurationProperties("spring.datasource.druid.master") public DataSource masterDataSource() { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); dataSource.setProxyFilters(Lists.newArrayList(statFilter())); return dataSource; } @Bean("slaveDataSource") @ConfigurationProperties("spring.datasource.druid.slave") public DataSource slaveDataSource() { DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); dataSource.setProxyFilters(Lists.newArrayList(statFilter())); return dataSource; } @Bean @Primary public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource firstDataSource, @Qualifier("slaveDataSource") DataSource secondDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(5); targetDataSources.put(DataSourceType.MASTER.name(), firstDataSource); targetDataSources.put(DataSourceType.SLAVE.name(), secondDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } @Bean public Filter statFilter() { StatFilter statFilter = new StatFilter(); //多长时间定义为慢sql,这里定义为5s statFilter.setSlowSqlMillis(5000); //是否打印出慢日志 statFilter.setLogSlowSql(true); //是否将日志合并起来 statFilter.setMergeSql(true); return statFilter; } //这是配置druid的监控 @Bean public ServletRegistrationBean servletRegistrationBean() { return new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); } }
DataSource
import com.table.common.enums.DataSourceType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义多数据源切换注解 * * @author ruoyi */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { /** * 切换数据源名称 */ public DataSourceType value() default DataSourceType.MASTER; }
DataSourceAspect
import com.table.common.StringUtils; import com.table.common.annotation.DataSource; import com.table.common.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.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 多数据源处理 * * @author ruoyi */ @Aspect @Order(1) @Component public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(com.table.common.annotation.DataSource)") public void dsPointCut() { } @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if (StringUtils.isNotNull(dataSource)) { DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); } try { return point.proceed(); } finally { // 销毁数据源 在执行方法之后 DynamicDataSourceContextHolder.clearDataSourceType(); } } }
DynamicDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 动态数据源 * * @author ruoyi */ 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(); } }
DynamicDataSourceContextHolder
import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 数据源切换处理 * * @author ruoyi */ 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.debug("切换到{}数据源", dsType); CONTEXT_HOLDER.set(dsType); } /** * 获得数据源的变量 */ public static String getDataSourceType() { return CONTEXT_HOLDER.get(); } /** * 清空数据源变量 */ public static void clearDataSourceType() { CONTEXT_HOLDER.remove(); } }
DataSourceType
public enum DataSourceType { /** * 主库 */ MASTER, /** * 从库 */ SLAVE }
1. 修改 DataSourceAspect 类中的扫描包地址
2. 使用方式在service impl方法上加注解 @DataSource(DataSourceType.MASTER)
不止是主从2个数据源,也可以增加数据源,理解下实现。
记录下理解:
自定义注解标识本次需要查询的数据库,通过aop把本次数据源保存在ThreadLocal中
继承AbstractRoutingDataSource 重写 determineCurrentLookupKey方法修改数据源,
从ThreadLocal中取数据源。
posted on 2019-07-23 13:50 xiaogui918 阅读(262) 评论(0) 编辑 收藏 举报