1、通过参考Spring的AbstractRoutingDataSource抽象类(该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上),重新构造一个AbstractMongoDBRoutingMongoTemplate抽象类,实现多mongdbTemplate的自动切换。
AbstractMongoDBRoutingMongoTemplate的源码:
/**
* @title: AbstractMongoDBRoutingMongoTemplate
* @company: 北京云知声信息技术有限公司
* @author: lizehao
* @date: 2016年10月18日
*/
public abstract class AbstractMongoDBRoutingMongoTemplate implements InitializingBean {
private Map<Object, Object> targetMongoTemplates;
private Object defaultTargetMongoTemplate;
private Map<Object, MongoTemplate> resolvedMongoTemplates;
private MongoTemplate resolvedDefaultMongoTemplate;
public void setTargetMongoTemplates(Map {
this.targetMongoTemplates = targetMongoTemplates;
}
@Override
public void afterPropertiesSet() {
if (this.targetMongoTemplates == null) {
throw new IllegalArgumentException("Property 'targetMongoTemplates' is required");
}
this.resolvedMongoTemplates = new HashMap<Object, MongoTemplate>(this.targetMongoTemplates.size());
for (Map.Entry {
Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
MongoTemplate mongoTemplate = resolveSpecifiedMongoTemplate(entry.getValue());
this.resolvedMongoTemplates.put(lookupKey, mongoTemplate);
}
if (this.defaultTargetMongoTemplate != null) {
this.resolvedDefaultMongoTemplate = resolveSpecifiedMongoTemplate(this.defaultTargetMongoTemplate);
}
}
protected Object resolveSpecifiedLookupKey(Object lookupKey) {
return lookupKey;
}
protected MongoTemplate resolveSpecifiedMongoTemplate(Object mongoTemplate) throws IllegalArgumentException {
if (mongoTemplate instanceof MongoTemplate) {
return (MongoTemplate) mongoTemplate;
} else {
throw new IllegalArgumentException(
"Illegal data source value - only [org.springframework.data.mongodb.core.MongoTemplate] and String supported: "
+ mongoTemplate);
}
}
protected MongoTemplate determineMongoTemplate() {
Assert.notNull(this.resolvedMongoTemplates, "mongoTemplate router not initialized");
Object lookupKey = determineCurrentLookupKey();
MongoTemplate mongoTemplate = this.resolvedMongoTemplates.get(lookupKey);
if (mongoTemplate == null && (lookupKey == null)) {
mongoTemplate = this.resolvedDefaultMongoTemplate;
}
if (mongoTemplate == null) {
throw new IllegalStateException("Cannot determine target MongoTemplate for lookup key [" + lookupKey + "]");
}
return mongoTemplate;
}
protected abstract Object determineCurrentLookupKey();
}
重点是determineMongoTemplate()方法,看代码:
这段源码的重点在于determineCurrentLookupKey()方法,这是AbstractMongoDBRoutingMongoTemplate 类中的一个抽象方法,而它的返回值是你所要用的MongoTemplate的key值,有了这个key值,resolvedMongoTemplates(这是个map,由配置文件中设置好后存入的)就从中取出对应的MongoTemplate,如果找不到,就用配置默认的数据源。
看完代码,应该有点启发了吧,没错!你要扩展AbstractMongoDBRoutingMongoTemplate类,并重写其中的determineCurrentLookupKey()方法,来实现MongoTemplate的切换:
/**
* @title: MongoDBJdbcTemplate
* @company: 北京云知声信息技术有限公司
* @author: lizehao
* @date: 2016年10月18日
*/
public class MongoDBTemplate extends AbstractMongoDBRoutingMongoTemplate {
public MongoDBTemplate() {
}
public MongoTemplate getMongoTemplate() {
return determineMongoTemplate();
}
@Override
protected Object determineCurrentLookupKey() {
return MongodbTemplateContextHolder.getMongoDBTemplate();
}
}
mongodbTemplateContextHolder这个类则是我们自己封装的对数据源进行操作的类:
/**
* @title: mongodbTemplateContextHolder
* @company: 北京云知声信息技术有限公司
* @author: lizehao
* @date: 2016年10月18日
*/
public class MongodbTemplateContextHolder{
private static final ThreadLocal contextHolder = new ThreadLocal();
public static void setMongoDBTemplate(String mongodbTemplateType) {
contextHolder.set(mongodbTemplateType);
}
public static String getMongoDBTemplate() {
return contextHolder.get();
}
public static void clearMongoDBTemplate() {
contextHolder.remove();
}
}
定义mongodbTemplate类型 使用的枚举
/**
* @title: mongoDB数据源类型
* @company: 北京云知声信息技术有限公司
* @author: lizehao
* @date: 2016年10月18日
*/
public enum MongoDBDataSource {
USER_CENTER("uc"), // 用户中心数据
DEVICE_CENTER("device"); // 设备中心数据
private final String value;
// 构造器默认也只能是private, 从而保证构造函数只能在内部使用
private MongoDBDataSource(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
2、有人就要问,那你setMongoDBTemplate这方法是要在什么时候执行呢?当然是在你需要切换mongodbTemplate的时候执行啦。手动在代码中调用写死吗?这是多蠢的方法,当然要让它动态咯。所以我们可以应用spring aop来设置,在dao层中需要切换mongodbTemplate的方法上:
/**
* @title: 多数据源动态切换
* @company: 北京云知声信息技术有限公司
* @author: lizehao
* @date: 2016年8月25日
*/
@Aspect
@Component
public class DynamicMongoDBDataSourceAspect {
private static final String PREFIX = ".dao.mongo.";
@Pointcut("execution(* com.unisound.iot.busi.web.dao.mongo..*.*(..))")
private void daoMethod() {
// do nothing
}
@Before("daoMethod()")
public void before(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
for (MongoDBDataSource mongoDBDataSource : MongoDBDataSource.values()) {
if (signature.getDeclaringTypeName().indexOf(PREFIX + mongoDBDataSource.getValue()) > -1) {
JdbcTemplateContextHolder.setJdbcTemplate(mongoDBDataSource.getValue());
break;
}
}
}
}
为了精简篇幅,省略了无关本内容主题的配置。spring-mongodb.xml
<bean id="mongoDBTemplate" class="com.unisound.iot.busi.web.common.dataSource.mongodbTemplate.MongoDBTemplate">
<property name="targetMongoTemplates">
<map key-type="java.lang.String">
<entry key="device" value-ref="deviceMongoTemplate">entry>
map>
property>
bean>
<mongo:mongo-client id="mongo" replica-set="${mongo.replicaSet}" credentials="${mongo.username}:${mongo.password}@${mongo.dbname}">
<mongo:client-options max-wait-time="${mongo.maxWaitTime}" socket-timeout="${mongo.socketTimeout}"
connect-timeout="${mongo.connectTimeout}" socket-keep-alive="${mongo.socketKeepAlive}" connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}" />
mongo:mongo-client>
<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<constructor-arg ref="mongo" />
<constructor-arg value="${mongo.dbname}" />
bean>
<bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="defaultMongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey">
<null />
constructor-arg>
bean>
<bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mappingContext" />
<property name="typeMapper" ref="defaultMongoTypeMapper" />
bean>
<bean id="deviceMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mongoConverter" ref="mappingMongoConverter" />
bean>