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