多数据源
多数据源 就是在分布式环境或者是集群环境下。你在不同的数据库(mysql DB2)上面配置了数据。在项目中要随意的切换数据源。这个就是多数据源。
就是你要切换不同版本数据库获取数据 这就是多数据源。
步骤如下:
1 创建一个springboot的工程
2 配置数据源信息
多数据源的思路如下:
(1) 装数据源的容器(map容器)
(2) 如果你不配置数据源的话 它默认走哪个数据源
(3) 通过determineTagegetDataSource方法 ,通过不同的标识key修改key去切换数据源
2 写一个配置类 使用@Configuration这个注解来标识你的这个类是一个配置类
1 在这个类中配置数据源信息
package com.sky.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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 javax.sql.DataSource; import java.util.HashMap; //加上这个注解的作用就是: 这个类是一个配置类 当服务器一启动的时候 就来加载这个类 @Configuration public class MyDataSourceAutoConfigguration { //使用bean组件 将这个类交给Spring管理 //配置数据源 当服务器一启动的时候 就把这些数据源加载引入进来 @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource(){ return DataSourceBuilder.create().build(); } //创建指定的数据源组件 @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource(){ return DataSourceBuilder.create().build(); } //这是一个主启动类 当服务一启动就来访问这个类 自上而下访问的 //所以你要加这个@Primary这个注解 先来访问这个方法 //将数据源塞到 map里面 @Bean @Primary //当服务器一启动的时候 它会先来启动这个组件 然后再去扫描 master数据源 和slave数据源 public DataSource primaryDataSource( @Autowired @Qualifier("masterDataSource") DataSource masterDataSource, @Autowired @Qualifier("slaveDataSource") DataSource slaveDataSource ){ RoutingDataSource routingDataSource = new RoutingDataSource(); HashMap map = new HashMap(); //这个key一定要跟上下文类里面的 还有yml文件里面的保持一致 map.put("master",masterDataSource); map.put("slave",slaveDataSource); routingDataSource.setTargetDataSources(map); return routingDataSource; } }
2
package com.sky.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; //创建AbstractRoutingDataSource 的子类 并重写determineCurrentLookkupKey()方法 去获取路由key,这个key在上下文类里面 //以实现动态切换数据源key //决定到底用哪个key public class RoutingDataSource extends AbstractRoutingDataSource { //determineCurrentLookupKey 决定你用哪个key 去上下文类里面获取 @Override protected Object determineCurrentLookupKey() { //获取上下文类里面的key return RoutingDataSourceContext.getDataSourceRoutingkey(); } }
3
package com.sky.config; //存储获取数据源的标识key //创建本地的线程 这个线程用来存储数据源的key 这个key是一个 public class RoutingDataSourceContext { //创建本地的线程 去加载的时候就有一个线程 只能有一个key 这个线程只能用来存储获取数据源的标识 static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); //指定数据源 给个构造方法 在加载的时候 它会去自动获取这个key public RoutingDataSourceContext(String key){ threadLocal.set(key); } //获取key public static String getDataSourceRoutingkey(){ String key = threadLocal.get(); //判断一下 如果你没有获取到这个key 那么就给默认的key master 如果有key 那就是master或者是其他的key return key == null ? "master" : key; } //用完数据源之后 去关闭 public void close(){ threadLocal.remove();; } }
在controller中是这样实现的
import com.sky.util.RoutingWith; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController public class ProductController { @Resource private ProductService productService; @RoutingWith("master") @RequestMapping("/findAllM") public List<Product> findAllM(){ // RoutingDataSourceContext master = new RoutingDataSourceContext("master"); return productService.findAllM(); } @RoutingWith("slave") @RequestMapping("/findAllS") public List<Product> findAllS(){ // 注释掉的这行代码是不是冗余了 所以这边使用自定义注解 RoutingDataSourceContext slave = new RoutingDataSourceContext("slave"); return productService.findAllS(); } }
自定义注解
package com.sky.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //这个注解只能在方法上使用 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) //在运行时生效 public @interface RoutingWith { //value() 属性 master这个属性对应的值 默认是master key如果不是master那就换成别的key String value() default "master"; }
Aop动态代理
package com.sky.util; import com.sky.config.RoutingDataSourceContext; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class RoutingAspect { //把刚刚自定义的注解 注入进来 ProceedingJoinPoint连接点 你哪个方法要注入到切面上面的那个连接点 //RoutingWith 你要注入的那个变量 不管哪个方法接入进来到切面里面 就可以直接使用注解· @Around("@annotation(routingWith)") public Object routingwithDataSource(ProceedingJoinPoint joinPoint ,RoutingWith routingWith ) throws Throwable { //获取到value() 对应的值 master String key = routingWith.value(); //把这个key塞到上下文类构造器里面注入进来 RoutingDataSourceContext ctx = new RoutingDataSourceContext(key); //返回接入点 return joinPoint.proceed(); } }