本文共 7124 字,大约阅读时间需要 23 分钟。
随着应用用户数量的不断增加,系统处理请求的并发压力也在上升。单一数据库逐渐暴露出性能瓶颈,这时候配置多个数据源成为一种有效的解决方案。Spring Boot 提供了丰富的功能,能够帮助开发者灵活配置多数据源。本文将详细介绍如何结合 AOP 和注解实现动态数据源切换。
首先,我们需要准备两个数据库,分别存储不同的数据。以下是数据库的建表语句和数据插入示例:
CREATE TABLE sys_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, user_name VARCHAR(255) NOT NULL, user_age INT DEFAULT 0);CREATE TABLE sys_user2 ( user_id INT PRIMARY KEY AUTO_INCREMENT, user_name VARCHAR(255) NOT NULL, user_age INT DEFAULT 0);INSERT INTO sys_user (user_name, user_age) VALUES ('张三', 25);INSERT INTO sys_user2 (user_name, user_age) VALUES ('李四', 30); 新建一个 Spring Boot 2.1.7.RELEASE 项目,添加以下依赖:
mysql mysql-connector-java 8.0.32 org.springframework.boot spring-boot-starter-jdbc org.springframework spring-aop 5.1.5.RELEASE org.aspectj aspectjweaver 1.9.2
在 application.properties 中配置数据源:
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/testdatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=falsespring.datasource.primary.username=rootspring.datasource.primary.password=rootspring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/testdatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=falsespring.datasource.secondary.username=rootspring.datasource.secondary.password=rootspring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
创建 DynamicDataSourceConfig 配置类:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.Map;@Configurationpublic class DynamicDataSourceConfig { @Autowired private DataSourceProperties dataSourceProperties; @Bean(name = "PrimaryDataSource") @Primary @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource getDateSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "SecondaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource getDateSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") public DynamicDataSource dataSource(@Qualifier("PrimaryDataSource") DataSource primary, @Qualifier("SecondaryDataSource") DataSource secondary) { Map targetDataSource = new HashMap<>(); targetDataSource.put(DataSourceType.DataBaseType.Primary, primary); targetDataSource.put(DataSourceType.DataBaseType.Secondary, secondary); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSource); dataSource.setDefaultTargetDataSource(primary); return dataSource; } @Bean(name = "SqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*/*.xml")); return bean.getObject(); }} 自定义 DynamicDataSource 类:
import org.springframework.boot.jdbc.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { DataSourceType.DataBaseType dataType = DataSourceType.getDataBaseType(); return dataType; }} DataSourceType 枚举类:
public enum DataSourceType { TYPE(new ThreadLocal<>()); private static final ThreadLocal TYPE; DataSourceType() { TYPE.set(this); } public static void setDataBaseType(DataSourceType dataType) { if (dataType == null) { throw new NullPointerException(); } TYPE.set(dataType); } public static DataSourceType getDataBaseType() { DataSourceType dataType = TYPE.get() == null ? Primary : TYPE.get(); return dataType; } public static void clearDataBaseType() { TYPE.remove(); }} 编写 mapper 接口,并添加自定义注解:
@Componentpublic interface PrimaryUserMapper { @DataSource List findAll();}@Componentpublic interface SecondaryUserMapper { @DataSource("secondary") List findAll();} 创建 DataSourceAop 类:
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class DataSourceAop { @Before("execution(*com.example.controller.UserController.primary(..))") public void setDataSource2test01() { System.err.println("Primary业务"); DataSourceType.setDataBaseType(DataSourceType.DataBaseType.Primary); } @Before("execution(*com.example.controller.UserController.secondary(..))") public void setDataSource2test02() { System.err.println("Secondary业务"); DataSourceType.setDataBaseType(DataSourceType.DataBaseType.Secondary); }} 启动项目,访问 http://localhost:8080/primary 和 http://localhost:8080/secondary,验证数据源切换是否正确。
创建自定义注解 DataSource:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataSource { String value() default "primary";} 创建 DynamicDataSourceAspect:
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); @Before("@annotation(dataSource)") public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable { String value = dataSource.value(); if (value.equals("primary")) { DataSourceType.setDataBaseType(DataSourceType.DataBaseType.Primary); } else if (value.equals("secondary")) { DataSourceType.setDataBaseType(DataSourceType.DataBaseType.Secondary); } else { DataSourceType.setDataBaseType(DataSourceType.DataBaseType.Primary); } } @After("@annotation(dataSource)") public void restoreDataSource(JoinPoint point, DataSource dataSource) throws Throwable { DataSourceType.clearDataBaseType(); }} 通过以上配置,开发者可以灵活管理多个数据源,动态切换数据源类型。这种方法不仅适用于方法级别,还可以扩展到类级别,实现更复杂的数据源管理策略。希望这篇文章能为您的项目带来帮助!
转载地址:http://esqfk.baihongyu.com/