- 由于我们会用到Spring Boot aop,因此在pom.xml中添加依赖
<!-- springboot-aop包,AOP切面注解,Aspectd等相关注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 在项目路径下新建 annotation 包,自定义注解便于切换数据源
package com.example.demo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解 */@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DS { String value() default "datasource1"; }
项目路径下新建config包
- 新建DataSourceContextHolder.java类,用于保存本地数据源
package com.example.demo.config; /** * 保存本地数据源 */public class DataSourceContextHolder { /** * 默认数据源 */ public static final String DEFAULT_DS = "datasource1"; /** * ThreadLocal之后会进行讲解 */ private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 设置数据源名 public static void setDB(String dbType) { System.out.println("切换到{"+dbType+"}数据源"); contextHolder.set(dbType); } // 获取数据源名 public static String getDB() { return (contextHolder.get()); } // 清除数据源名 public static void clearDB() { contextHolder.remove(); } }
- 新建DynamicDataSource.java类用于获取本地数据源
package com.example.demo.config;
import org.springframework. jdbc .datasource.lookup.AbstractRoutingDataSource;
/**
* 获取本地数据源
*/public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
System.out.println("数据源为"+DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
- 新建DataSourceConfig.java类读取配置
package com.example.demo.config; 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 org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 多数据源配置类 * */@Configuration public class DataSourceConfig { //数据源1 @Bean(name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.db1") // application.properteis中对应属性的前缀 public DataSource dataSource1() { return DataSourceBuilder.create().build(); } //数据源2 @Bean(name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.db2") // application.properteis中对应属性的前缀 public DataSource dataSource2() { return DataSourceBuilder.create().build(); } /** * 动态数据源: 通过AOP在不同数据源之间动态切换 * @return */ @Primary @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 默认数据源 dynamicDataSource.setDefaultTargetDataSource(dataSource1()); // 配置多数据源 Map<Object, Object> dsMap = new HashMap(); dsMap.put("datasource1", dataSource1()); dsMap.put("datasource2", dataSource2()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } /** * 配置@Transactional注解事物 * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
- 新建DynamicDataSourceAspect.java类用于自定义注解 + AOP的方式实现数据源动态切换。
package com.example.demo.config; import com.example.demo.annotation.DS; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 自定义注解 + AOP的方式实现数据源动态切换。 * */@Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(DS)") public void beforeSwitchDS(JoinPoint point, DS DS){ //获得当前访问的class Class<?> className = point.getTarget().getClass(); //获得访问的方法名 String methodName = point.getSignature().getName(); //得到方法的参数的类型 Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DEFAULT_DS; try { // 得到访问的方法对象 Method method = className.getMethod(methodName, argClass); // 判断是否存在@DS注解 if (method.isAnnotationPresent(DS.class)) { DS annotation = method.getAnnotation(DS.class); // 取出注解中的数据源名 dataSource = annotation.value(); } } catch (Exception e) { e.printStackTrace(); } // 切换数据源 DataSourceContextHolder.setDB(dataSource); } @After("@annotation(DS)") public void afterSwitchDS(JoinPoint point, DS DS){ DataSourceContextHolder.clearDB(); } }
以上代码需要配合application.yml中的配置。yml中数据源配置如下:
spring: datasource: db1: jdbc-url: jdbc: mysql :/localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL= false &failOverReadOnly=false username: test1 password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver db2: jdbc-url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&failOverReadOnly=false username: test2 password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver
- 在启动类中去掉数据源的自动配置:
- @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
- service方法小小的改造一下,在之前的基础上加上自定义注解,通过注解来动态切换数据源
@DS("datasource2") public List<User> testMapper () { return userMapper.selectAllUser(); }
启动项目,调用controller中的接口,从控制台的输出可以看到数据源已经实现了动态切换。