在之前的一片文章中介绍了使用AOP的方式来实现Redis的多数据源切换。而今天这一篇则是主要讲述Mongo
的多数据源切换。
使用AOP来实现Mongo的数据源切换与Redis的AOP切换相同,不同之处是需要替换MongoRepository
里面的MongoOperations
,从而实现多数据源的切换
代码示例
配置类,读取Mongo的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Configuration public class MongoMultiProperties {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoMultiProperties.class);
@Bean(name="mongodb1") @Primary @ConfigurationProperties(prefix = "spring.data.mongodb.db1") public MongoProperties db1Properties(){ LOGGER.info("正在初始化db1"); return new MongoProperties(); }
@Bean(name = "mongodb2") @ConfigurationProperties(prefix = "spring.data.mongodb.db2") public MongoProperties db2Properties(){ LOGGER.info("正在初始化db2"); return new MongoProperties(); }
}
|
配置MongoRepository
以下是为了演示,所以配置了两个MongoRepository,实际上使用了AOP的方式实现的多数据源,只需要配置一个默认的MongoRepository即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Configuration @EnableMongoRepositories(mongoTemplateRef = "mongoDB2") public class DB2Template {
@Autowired @Qualifier("mongodb2") private MongoProperties mongoProperties;
@Bean("mongoDB2") public MongoTemplate db2Template(){ return new MongoTemplate(db2Factory(mongoProperties)); }
@Bean public MongoDbFactory db2Factory(MongoProperties mongoProperties){ return new SimpleMongoDbFactory(new MongoClient(mongoProperties.getHost(),mongoProperties.getPort()),mongoProperties.getDatabase()); }
}
|
1 2 3 4
| @Repository public interface DB2Repository extends MongoRepository<MongoDB2,String>{ }
|
省略DB1Template
的配置,基本上都是差不多的
一般使用
上述的配置如果都OK的话,则可以直接使用@Autowired
注解使用。
1 2 3 4 5 6 7 8 9 10 11 12
| @Service public class MongoService { @Autowired DB2Repository db2Repository; @Autowired DB1Repository db1Repository;
public void mongoUpdate(){ db2Repository.save(new MongoDB2()); } }
|
但是使用AOP的方式的话,切Service
还是Repository
是需要选择的,首先因为在业务使用中,肯定是包含许多的Service
的,如果以后需要再添加其他的Service
,还需要添加切点,比较麻烦。
如果是切Repository
的话,那么这就好办了,直接配置一个主Repository,然后切这个主Repository,这样就可以将Service和AOP进行解耦。从而在Service里面,可以随意使用其他的数据源,例如:Mysql数据源,Redis数据源等。更加灵活
切面写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| @Slf4j @Component @Aspect @Order(-1) public class MongoAspect implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Around("execution(* com.somersames.config.mongo.db2.DB2Repository.*(..))") public Object doSwitch(ProceedingJoinPoint joinPoint) throws Throwable { return aopTest1(joinPoint);
}
private Object aopTest1(ProceedingJoinPoint joinPoint) throws Throwable { 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); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); Object singletonTarget = AopProxyUtils.getSingletonTarget(target); Field mongoOperationsField = singletonTarget.getClass().getDeclaredField("mongoOperations"); mongoOperationsField.setAccessible(true); modifiersField.setInt(mongoOperationsField,mongoOperationsField.getModifiers()&~Modifier.FINAL); mongoOperationsField.set(singletonTarget, applicationContext.getBean("mongoDB1")); return joinPoint.proceed(); }
private Object aopTest2(ProceedingJoinPoint joinPoint) throws Throwable { Field methodInvocationField = joinPoint.getClass().getDeclaredField("methodInvocation"); methodInvocationField.setAccessible(true); ReflectiveMethodInvocation o = (ReflectiveMethodInvocation) methodInvocationField.get(joinPoint); Field h = o.getProxy().getClass().getSuperclass().getDeclaredField("h"); h.setAccessible(true); AopProxy aopProxy = (AopProxy) h.get(o.getProxy()); Field advised = aopProxy.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object o2 = advised.get(aopProxy); if (o2 instanceof Advised) { Object o1 = ((Advised) o2).getTargetSource().getTarget(); Object o3 = AopProxyUtils.getSingletonTarget(o1); System.out.println(o3); Field mongoOperationsField = o3.getClass().getDeclaredField("mongoOperations"); mongoOperationsField.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(mongoOperationsField, mongoOperationsField.getModifiers() & ~Modifier.FINAL); mongoOperationsField.set(o3, applicationContext.getBean("mongoDB1")); } return joinPoint.proceed(); }
@Override public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
|
在上述的代码中,提供了两种的AOP的写法,但是最终都是获取mongoOperations
,然后通过applicationContext
来替换。
对比AOP的Redis写法,这里可以看到在Spring中的AOP
实现,最起码使用JDK动态代理
和Cglib
。所以在本文中,使用的是
Field h = o.getProxy().getClass().getSuperclass().getDeclaredField(“h”);
这个就是获取JDK动态代理的对象
至此,mongo的两种代理方式9最初版的代码编写完毕,后续可能需要对代码进行优化,从而避免每一次修改application.yml
都需要手动添加Repository
完整代码可以访问https://github.com/Somersames/Multi-Resource