前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >趣味编程|手写一个集成多数据源mongodb的 starter

趣味编程|手写一个集成多数据源mongodb的 starter

原创
作者头像
AI码师
修改2021-02-01 11:07:51
9491
修改2021-02-01 11:07:51
举报

关注公众号“AI码师”领取2021最新面试资料一份,公众号内回复“源码”,获取本项目源码

【前言】

主演:老王(技术总监),小码(本猿)

老王:小码啊,我们项目中需要使用到mongodb,你集成下吧,完成了和我说下。

小码:好的,一会就给你弄好。

小码三下五除二的给集成好了,然后给老王汇报了。

小码:王哥,我已经把mongodb集成好了。

老王:好的,现在由于我们项目中会用到很多mongo数据库,你现在集成的mongo支持多数据源动态切换么?

小码:这个,这个,啥叫多数据源动态切换啊?

老王:就是在运行过程中,能够根据需要动态去连接哪个数据库,咱们项目需要支持多个特性,如果你对这个不太清楚的话,我给你一个思路,你可以考虑使用切面来实现,具体怎么弄,你自己研究下.

小码:好的,王哥。

小码想了很久,各种百度,终于找到了解决方案,花了一上午的时间,终于弄完了,又去给老王汇报了。

小码:王哥,现在项目中的mongo已经实现了多数据源了(哈哈,心里很自豪)。

老王:小伙子,很快嘛,不过现在又来一个任务,你需要把你集成的这个功能封装成一个starter,另外一个项目也需要使用这个功能,你抽时间封装下吧。

小码:好的,王哥,保证完成任务

小码下去之后,就开始研究怎么去封装成一个starter,下班之前弄好了,不过这次他没去找老王了,准备第二天再去,不然又得加班,哈哈!!!

【正文】

前面水了那么多,主要是给大家设置一种场景,让同志们知道为啥要去做这么一个功能,现在就直接进入正题了:

【springboot集成mongodb】

  • 引入mongodb依赖
代码语言:javascript
复制
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
  • 配置mongodb连接信息,在application.yml中配置
代码语言:javascript
复制
# 设置了用户名和密码的连接
spring:
  data:
    mongodb:
      uri: mongodb://用户名:密码@IP:PORT/数据库?authSource=${auth_db:用户认证数据库}
# 没有设置用户名和密码的连接配置
spring:
    data:
        mongodb:
            uri: mongodb://IP:PORT/数据库
  • 写测试代码

我们创建一个接口,然后在接口方法中去操作monog库:

接口中,直接引入MongoTemplate,就可以直接操作mongo了,这里对mongo如何使用不做过多介绍。

代码语言:javascript
复制
/**
 * Created by AI码师 on 2019/4/19.
 * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
 * @return
 */
@RequestMapping("/home")
@RestController
public class HomeController {
    @Autowired
    private MongoTemplate mongoTemplate;
    @PostMapping
    public String addData(@RequestParam(value = "name") String name,@RequestParam(value = "addr") String addr,@RequestParam(value = "email") String email){
        Student student = new Student();
        student.setAddr(addr);
        student.setName(name);
        student.setEmail(email);
        mongoTemplate.insert(student);
        return "添加成功";
    }
}

请求接口:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

响应数据:

在这里插入图片描述
在这里插入图片描述

响应添加成功,我们看下数据库,是否添加上去了:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

数据已经添加上去了,说明已经集成成功了,但这还是第一步,我们需要做的是支持多数据源,接下来我们一起来完成逼格更高的多数据源mongo吧。

【实现多数据源】

实现思路

先介绍下实现多数据源动态切换的思路:

首先通过AOP技术,在调用方法前后动态替换mongo数据源,这个主要是替换mongo中mongodbfactory(SimpleMongoClientDatabaseFactory)值,每个factory都维护自己需要连接的库,如果在操作之前,替换该参数为自己需要操作的数据库factory,操作结束又切换成原来的,不就可以实现动态切换数据源了么。

说完了思路,我们直接上代码吧

垒代码
  • 添加aop依赖
代码语言:javascript
复制
     <!--引入AOP依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  • 修改数据库连接配置
代码语言:javascript
复制
# 设置了用户名和密码的连接
spring:
  data:
    mongodb:
      uri: mongodb://用户名:密码@IP:PORT/#?authSource=${auth_db:用户认证数据库}
# 没有设置用户名和密码的连接配置
spring:
    data:
        mongodb:
            uri: mongodb://IP:PORT/#
            
与上述配置,做了小小的改动,将操作的数据库名称替换成了#,用来做后续备用
  • 创建切面
代码语言:javascript
复制
package com.aimashi.dynamicmongo.config;

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.core.MongoDatabaseFactorySupport;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by AI码师 on 2019/4/19.
 * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
 * @return
 */
@Component
@Aspect
public class MongoSwitch {
  private final Logger logger = LoggerFactory.getLogger(MongoSwitch.class);

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

  // @Pointcut("@annotation(com.pig4cloud.pig.common.log.annotation.MongoLog)")
  @Pointcut("execution(public * com.aimashi.dynamicmongo.config.MongotemplteService.*(..))")
  public void routeMongoDB() {}

  @Around("routeMongoDB()")
  public Object routeMongoDB(ProceedingJoinPoint joinPoint) {
    Object result = null;
    // 获取需要访问的项目数据库
    String dbName = (String) joinPoint.getArgs()[0];
    Object o = joinPoint.getTarget();
    Field[] fields = o.getClass().getDeclaredFields();
    MultiMongoTemplate mongoTemplate = null;

    try {
      for (Field field : fields) {
        field.setAccessible(true);

        Class fieldclass = field.getType();
        // 找到Template的变量
        if (fieldclass == MongoTemplate.class || fieldclass == MultiMongoTemplate.class) {
          // 查找项目对应的MongFactory
          SimpleMongoClientDatabaseFactory simpleMongoClientDbFactory = null;
          // 实例化
          if (templateMuliteMap.get(dbName) == null) { // 替换数据源
            simpleMongoClientDbFactory =
                new SimpleMongoClientDatabaseFactory(this.uri.replace("#", dbName));
            templateMuliteMap.put(dbName, simpleMongoClientDbFactory);
          } else {
            simpleMongoClientDbFactory =
                (SimpleMongoClientDatabaseFactory) templateMuliteMap.get(dbName);
          }
          // 如果第一次,赋值成自定义的MongoTemplate子类
          if (fieldclass == MongoTemplate.class) {
            mongoTemplate = new MultiMongoTemplate(simpleMongoClientDbFactory);
          } else if (fieldclass == MultiMongoTemplate.class) {
            Object fieldObject = field.get(o);
            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);
        mongoTemplate.removeMongoDbFactory();
      }
    } catch (Exception e) {
      logger.error("", e);
    }

    return result;
  }
}
  • 创建相关配置类
代码语言:javascript
复制
package com.aimashi.dynamicmongo.config;

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
/**
 * Created by AI码师 on 2019/4/19.
 * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
 * @return
 */
@Service
public class MongotemplteService {
  private MongoTemplate mongoTemplate;
  public <T> T save(String dbName, T var1) {
    return mongoTemplate.save(var1);
  }
}
代码语言:javascript
复制
package com.aimashi.dynamicmongo.config;

import com.mongodb.client.MongoDatabase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoDatabaseFactorySupport;
import org.springframework.data.mongodb.core.MongoTemplate;

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

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

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

    @Override
    public MongoDatabase getDb() {
        return mongoDbFactoryThreadLocal.get().getMongoDatabase();
    }
}
  • 添加测试接口
代码语言:javascript
复制
/**
 * Created by AI码师 on 2019/4/19.
 * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
 * @return
 */
// dbName 为数据库名称
   @PutMapping
    public String addDataByDynamic(@RequestParam(value = "dbName") String dbName,@RequestParam(value = "name") String name,@RequestParam(value = "addr") String addr,@RequestParam(value = "email") String email){
        Student student = new Student();
        student.setAddr(addr);
        student.setName(name);
        student.setEmail(email);
        mongotemplteService.insert(dbName,student);
        return "添加成功";
    }

请求接口:数据库名参数传了ams1

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

请求响应:响应成功

在这里插入图片描述
在这里插入图片描述

我们看下数据库,发现在数据库ams1下面已经有了此数据:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

我们将数据库名参数修改为:ams2,进行请求

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发现数据源已经切换成功了。

到这里,大家有没有发现自己很牛逼了啊,不过本篇文章还没算完,现在虽然已经实现了动态切换数据源的功能,但是还只能在自己项目上用,别的项目需要使用,只能直接复制过去,我们接下来需要做一个更牛逼的事情:手写一个starter来封装这个功能,别人只需要引入依赖,即可开箱即用:

【整合到starter里面】

  • 创建一个maven项目:dynamicmongo-starter

将如下文件拷贝到新项目中

在这里插入图片描述
在这里插入图片描述
  • 创建自动装配文件
代码语言:javascript
复制
package com.aimashi.dynamicmongo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by AI码师 on 2019/4/19.
 * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
 * @return
 */
@Configuration(proxyBeanMethods = false)
public class MongodbAutoConfiguration {

 @Bean
 public MongoSwitch mongoSwitch() {
  return new MongoSwitch();
 }
 @Bean
 public MongotemplteService mongotemplteService() {
  return new MongotemplteService();
 }

}
  • 新建 resources/META_INF/spring.factories 文件
代码语言:javascript
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.aimashi.dynamicmongo.config.MongodbAutoConfiguration

到这里starter已经编写完成,是不是很简单。。

【使用starter】

starter已经编写好,我们只需要在项目中引入该依赖

代码语言:javascript
复制
        <dependency>
            <groupId>com.aimashi</groupId>
            <artifactId>dynamicmongo-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

然后在需要操作mongod方法的地方,引入:MongotemplteService即可;

注意 MongotemplteService 里面的方法大家按需扩充,目前只写了一个,大家使用的时候,只需要把mongoTemplate里面的方法名写到MongotemplteService中,然后再去调用mongoTemplate里面对应方法即可。

【总结】

很少写这么长的实践类文章,现在已经十一点半了,该休息了,后面会有更多文章和大家一起分享,希望大家能有所收获,晚安!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【前言】
  • 【正文】
    • 【springboot集成mongodb】
      • 【实现多数据源】
        • 实现思路
        • 垒代码
      • 【整合到starter里面】
        • 【使用starter】
        • 【总结】
        相关产品与服务
        云数据库 MongoDB
        腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档