最近公司项目须要支持multi-tenancy,每一个tenant数据库地址不一样,需求就是修改配置文件,添加或者删除数据库配置,重启系统后就能够完成,不须要额外修改代码。html
网上搜索了一下,基本上都是将配置写在了代码里了,例如:http://www.devzxd.top/2017/06...,不能动态增减数据库配置,而且对不一样的数据库建立不一样的Repository。继续Google以后,在github上找到一个例子multi-tenant-spring-mongodb,看了一下代码,仍是不符合需求。没办法,只能看代码,调试来看看本身能不能实现这个需求了。java
那spring-data-mongodb怎么来完成数据库的访问的呢?核心类是SimpleMongoRepository,而mongoOperations变量才是完成访问的关键,而这个mongoOperations其实就是mongoTemplate。那就意味着若是咱们可以动态改变这个mongoTemplate的值就能够切换数据库了,窃喜。git
解决的方法有了,可是如何实现呢?库并无提供相似Hibernate那样的MultiTenantConnectionProvider
和CurrentTenantIdentifierResolver
来帮助咱们实现。既然spring是经过AOP和Proxy来完成功能的调用的,咱们彷佛也能够这么玩。github
AopProxyUtils.getSingletonTarget(target)
取到最终的SimpleMongoRepository
实例。代码以下:spring
@Repository public interface WidgetDataRepository extends MongoRepository<WidgetData, String> { }
// 注意:线程不安全!!! @Around("execution(* com.example.demo.dao.mongo.MongoWidgetDataRepo.*(..))") public Object doSwitch(ProceedingJoinPoint joinPoint) throws Throwable { // 拿到咱们须要的tenant String tenant = (String) RequestContextHolder.currentRequestAttributes().getAttribute("tenant", SCOPE_REQUEST); tenant = tenant == null ? "test" : tenant; // 经过反射获取到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,并往里面填入值 Object singletonTarget = AopProxyUtils.getSingletonTarget(target); Field mongoOperationsField = singletonTarget.getClass().getDeclaredField("mongoOperations"); mongoOperationsField.setAccessible(true); mongoOperationsField.set(singletonTarget, ac.getBean("mongoTemplate" + tenant)); return joinPoint.proceed(); }
这样咱们就能够完成数据库的切换了,能够利用spring-data-mongodb,尽量的少些模板代码。mongodb