我的springboot web项目工程目录如下:
POM文件如下:
4.0.0 cn.example SpringBootStudy 1.0-SNAPSHOT war SpringBootStudy Maven Webapp http://www.example.com UTF-8 1.8 1.8 1.1.16 org.springframework.boot spring-boot-starter-parent 2.3.3.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok org.springframework.boot spring-boot-starter-jdbc mysql mysql-connector-java runtime com.alibaba druid-spring-boot-starter ${druid.starter.version} org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-aop org.aspectj aspectjrt org.springframework.boot spring-boot-maven-plugin
YAML配置文件如下:
spring:
profiles:
active: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #修改数据源为Druid
druid:
master:
url: jdbc:mysql://localhost:3306/study?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
username: root
password: root
slave:
enable: true
url: jdbc:mysql://localhost:3306/study?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
username: root
password: root
initialSize: 3 #修改Druid的默认连接配置
minIdle: 10 # 最小连接池数量
maxActive: 20 # 最大连接池数量
maxWait: 60000 # 配置获取连接等待超时的时间
timeBetweenEvictionRunsMillis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒
在annotation包中创建自定义注解DataSource:
package cn.xhh.study.common.annotation;
import cn.xhh.study.common.enums.DataSourcesType;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
public @interface DataSource {
DataSourcesType name() default DataSourcesType.MASTER;
}
在enums包中创建枚举类DataSourcesType:
package cn.xhh.study.common.enums;
public enum DataSourcesType {
MASTER,
SLAVE
}
在core.aop包中创建切面类DynamicDataSourceAspect:
package cn.xhh.study.core.aop;
import cn.xhh.study.common.annotation.DataSource;
import cn.xhh.study.core.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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;
@Aspect
@Component
@Order(-1)
public class DynamicDataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(cn.xhh.study.common.annotation.DataSource)"
+ "|| @within(cn.xhh.study.common.annotation.DataSource)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Method targetMethod = this.getTargetMethod(point);
//获取要切换的数据源
DataSource dataSource = targetMethod.getAnnotation(DataSource.class);
if (dataSource != null) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.name().name());
}
try {
return point.proceed();
}
finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.removeDataSourceType();
}
}
private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method agentMethod = methodSignature.getMethod();
return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());
}
}
在core.datasource包中创建动态数据源类DynamicDataSource和数据源切换处理类DynamicDataSourceContextHolder:
package cn.xhh.study.core.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
public static DynamicDataSource build() {
return new DynamicDataSource();
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
package cn.xhh.study.core.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicDataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
private static final ThreadLocal contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
log.info("已切换到{}数据源", dataSourceType);
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void removeDataSourceType() {
contextHolder.remove();
}
}
在core.druid包中创建数据源配置类DataSourceConfiguration和数据源配置文件类DataSourceProperties:
package cn.xhh.study.core.druid;
import cn.xhh.study.common.enums.DataSourcesType;
import cn.xhh.study.core.datasource.DynamicDataSource;
import cn.xhh.study.core.druid.DataSourceProperties;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
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.DependsOn;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DataSourceProperties dataSourceProperties) {
return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());
}
@Bean
@ConditionalOnProperty( prefix = "spring.datasource.druid.slave", name = "enable", havingValue = "true")//是否开启数据源开关---若不开启 默认适用默认数据源
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slaveDataSource(DataSourceProperties dataSourceProperties) {
return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());
}
@Bean(name = "dynamicDataSource")
@Primary
@DependsOn({"masterDataSource","slaveDataSource"})
public DynamicDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map
package cn.xhh.study.core.druid;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Setter
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DataSourceProperties {
private int initialSize;
private int minIdle;
private int maxActive;
private int maxWait;
private int timeBetweenEvictionRunsMillis;
private int minEvictableIdleTimeMillis;
private int maxEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
public DruidDataSource setDataSource(DruidDataSource datasource) {
// 配置初始化大小、最小、最大
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
// 配置获取连接等待超时的时间
datasource.setMaxWait(maxWait);
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// 配置一个连接在池中最小、最大生存的时间,单位是毫秒
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}
在启动类StudyApplication加入注解,解决循环依赖:
package cn.xhh.study;
import cn.xhh.study.servlet.listener.MyListener;
import cn.xhh.study.servlet.MyServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
@ServletComponentScan
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}
添加Controller测试类:
package cn.xhh.study.controller;
import cn.xhh.study.common.annotation.DataSource;
import cn.xhh.study.common.enums.DataSourcesType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
@Controller
public class JdbcTemplateController {
private JdbcTemplate jdbcTemplate;
public JdbcTemplateController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@ResponseBody
@RequestMapping("queryUserForList")
@DataSource(name = DataSourcesType.SLAVE)
public List
自己在mysql中建立一个user表,进行测试。
在浏览器输入地址,查看结果:
http://localhost:8081/queryUserForList
后台程序打印日志:
2021-12-08 10:58:13.400 INFO 7284 --- [nio-8081-exec-1] c.x.s.c.d.DynamicDataSourceContextHolder : 已切换到SLAVE数据源
Druid多数据源配置成功!



