springboot集成mongodb实现动态切换数据源

主要实现原理,利用spring的aop 在切入点执行db操作之前 将数据库切换:

本例子采用aop在controller进行拦截 拦截到MongoTemplate.class 切换数据源后重新放回去 ,处理完成后将相关数据源的template删除

 

引入mongodb相关依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
<!--引入AOP依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
多数据源MultiMongoTemplate
import com.mongodb.client.MongoDatabase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;

public class MultiMongoTemplate extends MongoTemplate {
    private Logger logger= LoggerFactory.getLogger(MultiMongoTemplate.class);
//用来缓存当前MongoDbFactory
    private static ThreadLocal<MongoDbFactory> mongoDbFactoryThreadLocal;
    public MultiMongoTemplate(MongoDbFactory mongoDbFactory){
        super(mongoDbFactory);
        if(mongoDbFactoryThreadLocal==null) {
            mongoDbFactoryThreadLocal = new ThreadLocal<>();
        }
    }

    public void setMongoDbFactory(MongoDbFactory factory){
        mongoDbFactoryThreadLocal.set(factory);
    }

    public void removeMongoDbFactory(){
        mongoDbFactoryThreadLocal.remove();
    }

    @Override
    public MongoDatabase getDb() {
        return mongoDbFactoryThreadLocal.get().getDb();
    }
}

aop 切片类MongoSwitch

import cn.net.topnet.topfs.dynamicdb.MultiMongoTemplate;
import com.mongodb.MongoClient;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
public class MongoSwitch {
    private final Logger logger = LoggerFactory.getLogger(MongoSwitch.class);

    @Autowired
    private MongoDbFactory mongoDbFactory;
    private Map<String,MongoDbFactory> templateMuliteMap=new HashMap<>();
//获取配置文件的副本集连接
    @Value("${spring.data.mongodb.uri}")
    private String uri;

    @Pointcut("execution(* cn.net.topnet.topfs.controller..*.*(..))")
    public void routeMongoDB() {

    }

    @Around("routeMongoDB()")
    public Object routeMongoDB(ProceedingJoinPoint joinPoint) {
        Object result = null;
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        //获取需要访问的项目数据库
        String dbName = request.getRequestURI().trim().substring(1);
        String name = joinPoint.getSignature().getName();
        Object o = joinPoint.getTarget();
        Field[] fields = o.getClass().getDeclaredFields();
        MultiMongoTemplate mongoTemplate = null;

        try {
            for (Field field : fields) {
                field.setAccessible(true);
                Object fieldObject = field.get(o);
                Class fieldclass = fieldObject.getClass();
                //找到Template的变量
                if (fieldclass == MongoTemplate.class || fieldclass == MultiMongoTemplate.class) {
                    //查找项目对应的MongFactory
                    SimpleMongoClientDbFactory simpleMongoClientDbFactory=(SimpleMongoClientDbFactory)templateMuliteMap.get(dbName);
                    //实例化
                    if(simpleMongoClientDbFactory==null){
                //替换数据源 simpleMongoClientDbFactory
= new SimpleMongoClientDbFactory(this.uri.replace("#",dbName)); templateMuliteMap.put(dbName,simpleMongoClientDbFactory); } //如果第一次,赋值成自定义的MongoTemplate子类 if(fieldclass==MongoTemplate.class){ mongoTemplate = new MultiMongoTemplate(simpleMongoClientDbFactory); }else if(fieldclass==MultiMongoTemplate.class){ mongoTemplate=(MultiMongoTemplate)fieldObject; } //设置MongoFactory mongoTemplate.setMongoDbFactory(simpleMongoClientDbFactory); //重新赋值 field.set(o, mongoTemplate); break; } } try { result = joinPoint.proceed(); //清理ThreadLocal的变量 mongoTemplate.removeMongoDbFactory(); } catch (Throwable t) { logger.error("", t); } } catch (Exception e) { logger.error("", e); } return result; } }

yml配置

spring:
  data:
    mongodb:
      uri: mongodb://bobo:bobo123@192.168.3.114:27017,192.168.3.114:27018,192.168.3.114:27019/#?connect=replicaSet&slaveOk=true&replicaSet=myrs

测试controller

    @GetMapping("/{dbName}")
    public  void testMongoTemplate(@PathVariable("dbName") String dbName){

        Query query = new Query();
        query.addCriteria(Criteria.where("display").is("测试"));
        // spring会将查询到的结果自动映射
        Domains one = mongoTemplate.findOne(query, Domains.class);

        System.out.println(one);
    }

 

posted @ 2020-04-09 08:55  valar-dohaeris  阅读(5764)  评论(3编辑  收藏  举报