AOP+Nacos实现动态数据源切换
1. 添加依赖
<!-- Nacos配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2022.0.0.0</version>
</dependency>
<!-- 动态数据源(支持多数据源切换) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.2.0</version>
</dependency>
2. 配置Nacos连接
bootstrap.yml
中配置Nacos服务地址及数据源配置文件:
spring:
application:
name: dynamic-datasource-demo
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
# 数据源配置文件ID(需在Nacos控制台提前创建)
data-id: datasource-config.yaml
file-extension: yaml
refresh-enabled: true # 开启动态刷新
3. Nacos配置内容
在Nacos控制台创建datasource-config.yaml
文件,内容如下:
datasource:
dynamic:
primary: master # 默认数据源
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/master?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave1:
url: jdbc:mysql://127.0.0.1:3306/slave1?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
4. 动态数据源配置类
@Configuration
@Slf4j
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
public DataSource dynamicDataSource() {
// 自动读取Nacos配置中的datasource节点
return new DynamicRoutingDataSource();
}
// 监听Nacos配置变更,刷新数据源
@NacosConfigListener(dataId = "datasource-config.yaml", groupId = "DEFAULT_GROUP")
public void onConfigChanged(String newConfig) {
log.info("检测到数据源配置变更:{}", newConfig);
DynamicDataSourceContextHolder.clear();
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dynamicDataSource();
ds.reload(); // 核心:触发数据源重载
}
}
5. 数据源上下文管理器
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setDataSourceKey(String key) {
CONTEXT.set(key);
}
public static String getDataSourceKey() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
6. 动态切换实现(AOP切面)
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.annotation.DS)")
public void dataSourcePointCut() {}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
DS ds = signature.getMethod().getAnnotation(DS.class);
String oldKey = DynamicDataSourceContextHolder.getDataSourceKey();
DynamicDataSourceContextHolder.setDataSourceKey(ds.value());
try {
return joinPoint.proceed();
} finally {
DynamicDataSourceContextHolder.setDataSourceKey(oldKey);
}
}
}
7. 自定义注解@DS
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {
String value() default "master";
}
8. Service层使用示例
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@DS("master")
public void insert(User user) {
userMapper.insert(user); // 写入主库
}
@DS("slave1")
public User getById(Long id) {
return userMapper.selectById(id); // 从slave1读取
}
}