多数据源

多数据源 就是在分布式环境或者是集群环境下。你在不同的数据库(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;

    }
}

 

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();
    }
}

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();
    }
}
posted @ 2022-05-04 23:35  秃头少女and战战  阅读(809)  评论(0编辑  收藏  举报