關注公眾號“AI碼師”領取2021最新面試資料一份,公眾號內回復“原始碼”,獲取本專案原始碼
【前言】
主演:老王(技術總監),小碼(本猿)
老王:小碼啊,我們專案中需要使用到mongodb,你集成下吧,完成了和我說下,
小碼:好的,一會就給你弄好,
小碼三下五除二的給集成好了,然后給老投訓報了,
小碼:王哥,我已經把mongodb集成好了,
老王:好的,現在由于我們專案中會用到很多mongo資料庫,你現在集成的mongo支持多資料源動態切換么?
小碼:這個,這個,啥叫多資料源動態切換啊?
老王:就是在運行程序中,能夠根據需要動態去連接哪個資料庫,咱們專案需要支持多個特性,如果你對這個不太清楚的話,我給你一個思路,你可以考慮使用切面來實作,具體怎么弄,你自己研究下.
小碼:好的,王哥,
小碼想了很久,各種百度,終于找到了解決方案,花了一上午的時間,終于弄完了,又去給老投訓報了,
小碼:王哥,現在專案中的mongo已經實作了多資料源了(哈哈,心里很自豪),
老王:小伙子,很快嘛,不過現在又來一個任務,你需要把你集成的這個功能封裝成一個starter,另外一個專案也需要使用這個功能,你抽時間封裝下吧,
小碼:好的,王哥,保證完成任務
小碼下去之后,就開始研究怎么去封裝成一個starter,下班之前弄好了,不過這次他沒去找老王了,準備第二天再去,不然又得加班,哈哈!!!
【正文】
前面水了那么多,主要是給大家設定一種場景,讓同志們知道為啥要去做這么一個功能,現在就直接進入正題了:
【springboot集成mongodb】
- 引入mongodb依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
- 配置mongodb連接資訊,在application.yml中配置
# 設定了用戶名和密碼的連接
spring:
data:
mongodb:
uri: mongodb://用戶名:密碼@IP:PORT/資料庫?authSource=${auth_db:用戶認證資料庫}
# 沒有設定用戶名和密碼的連接配置
spring:
data:
mongodb:
uri: mongodb://IP:PORT/資料庫
- 寫測驗代碼
我們創建一個介面,然后在介面方法中去操作monog庫:
介面中,直接引入MongoTemplate,就可以直接操作mongo了,這里對mongo如何使用不做過多介紹,
/**
* 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依賴
<!--引入AOP依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 修改資料庫連接配置
# 設定了用戶名和密碼的連接
spring:
data:
mongodb:
uri: mongodb://用戶名:密碼@IP:PORT/#?authSource=${auth_db:用戶認證資料庫}
# 沒有設定用戶名和密碼的連接配置
spring:
data:
mongodb:
uri: mongodb://IP:PORT/#
與上述配置,做了小小的改動,將操作的資料庫名稱替換成了#,用來做后續備用
- 創建切面
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;
}
}
- 創建相關配置類
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);
}
}
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();
}
}
- 添加測驗介面
/**
* 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
將如下檔案拷貝到新專案中

- 創建自動裝配檔案
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 檔案
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.aimashi.dynamicmongo.config.MongodbAutoConfiguration
到這里starter已經撰寫完成,是不是很簡單,,
【使用starter】
starter已經撰寫好,我們只需要在專案中引入該依賴
<dependency>
<groupId>com.aimashi</groupId>
<artifactId>dynamicmongo-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后在需要操作mongod方法的地方,引入:MongotemplteService即可;
注意 MongotemplteService 里面的方法大家按需擴充,目前只寫了一個,大家使用的時候,只需要把mongoTemplate里面的方法名寫到MongotemplteService中,然后再去呼叫mongoTemplate里面對應方法即可,
【總結】
很少寫這么長的實踐類文章,現在已經十一點半了,該休息了,后面會有更多文章和大家一起分享,希望大家能有所識訓,晚安!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/255201.html
標籤:java
