Spring boot MongoDB多数据源,MongoRepository实现

https://blog.csdn.net/qq_35124119/article/details/83210772


背景
        最近项目中有需求,需要多个mongo库,分割数据。网上有很多文章可参考,其原理是:在Spring 容器中实例化多个MongoTemplate,代码示例:

@Configuration
@EnableMongoRepositories(basePackages = {"com.sunliangliang.service.basic"}, mongoTemplateRef = "basicMongoTemplate")
@ConfigurationProperties(prefix = "basic.mongodb")
public class BasicMongoConfigure extends AbstractMongoConfigure {
@Override
@Bean(name = "basicMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}

使用方式:

1.使用时通过@Autowired 和@Qualifier注入MongoTemplate实例以操作不同的mongo数据库(PS:可使用@Resource注解,引入MongoTemplate 并为其实例命名为所需的已配置的bean名称,而引入相对应的MongoTemplate实例)

2.通过MongoTemplate示例配置的basePackages指定继承了MongoRepository的model接口,在操作数据库时,使用所配置的MongoTemplate实例,以操作不同的数据库。

这是最常见的Springboot Mongo多数据配置方法,但是项目中主项目因引入的子项目里已有配置,导致在主项目中MongoTemplate配置无效,MongoRepository无法按配置的basePackages路径使用对应的MongoTemplate操作mongo库。

分析
         前述的实现mongo多数据库,核心是操作不同的MongoTemplate。除了直接使用MongoTemplate,我们通常会写一个接口继承MongoRepository接口,并且不需要实现该接口就可操作数据库。实际上在使用继承的MongoRepository接口的业务接口访问mongo时,使用的是SpringData的SimpleMongoRepository类的实例来操作数据库,而SimpleMongoRepository的一个非常重要的Filed是MongoOperations接口,而MongoTemplate正是MongoOperations的实现。SimpleMongoRepository也正是使用MongoOperations操作mongo数据库的。

思路
      在SimpleMongoRepository操作数据库时,动态的修改其MongoOperations的值,即MongoTemplate。所以Spring AOP出场了。

解决办法
1.为项目引入Spring AOP

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.编写AOP代码

package com.mongo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

import java.lang.reflect.Field;

/**
* 通过AOP操作,动态更改更改mongo的repository层mongoTemplate<br/>
* 以实现mongo分库
*
* @author RangoLan
* @desciption
* @date Created in 2018/10/17 15:20
*/
@Aspect
@Component
public class RepositoryAop {
@Autowired
WebApplicationContext context;


@Around("execution(* com.mongo.basic..*.*(..))")
public Object setMongoOperations(ProceedingJoinPoint joinPoint) throws Throwable {
setMongoTemplate4Repository(joinPoint, (MongoTemplate) context.getBean(AdminConfiguration.MONGO_ADMIN));

return joinPoint.proceed();
}

private void setMongoTemplate4Repository(ProceedingJoinPoint joinPoint, MongoTemplate template) throws NoSuchFieldException, IllegalAccessException {
// 通过反射获取到target
Field methodInvocationField = joinPoint.getClass().getDeclaredField("methodInvocation");
methodInvocationField.setAccessible(true);
ReflectiveMethodInvocation o = (ReflectiveMethodInvocation) methodInvocationField.get(joinPoint);

Field targetField = o.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(o);

// 获得SimpleMongoRepository,并往里面填入指定mongoTemplate
Object singletonTarget = AopProxyUtils.getSingletonTarget(target);
Field mongoOperationsField = singletonTarget.getClass().getDeclaredField("mongoOperations");
mongoOperationsField.setAccessible(true);
mongoOperationsField.set(singletonTarget, template);
}


}
单元测试OK。

注意:  由于SimpleMongoRepository在Spring容器中为单例,aop切点代码执行后,SimpleMongoRepository的MongoOperations已更改,其他未配置切点的Repository在操作mongo时,数据库会混乱。所以应该在AOP中明确配置各Repository所使用的MongoTemplate。

 
后记
         AOP方式实现,可实现Repository层的mongodb分库,但是相较于常规做法,还是比较麻烦一点,建议构建项目时结构应尽量合理些。相较于配置MongoTemplate bean时指定basePackages,AOP可做到方法级控制,当然这是AOP的优点,但是对于数据库来说,一个表或者collection的所有操作不该是在同一个库么?
————————————————
 
原文链接:https://blog.csdn.net/qq_35124119/article/details/83210772

posted @ 2020-07-17 14:24  kelelipeng  阅读(1730)  评论(0编辑  收藏  举报