主要實現原理,利用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); }