mongo多数据源实现框架,其它数据库也可参考自行实现。

获取MongoTemplate的逻辑(数据源需要事先全部配置好,并对应写死的key,如下dao1,dao2),主要是参考网上的写法适当做修改:http://blog.csdn.net/qq994406030/article/details/52861421。aop拦截提供统一的代码,框架会扫描所有实现DecisionHandler的所有bean,该接口由业务代码决定,参考SpecifiedDecisionHandler。

配置如下:用户要改的是扫描的路径,用哪个数据库由用户自己实现的DecisionHandler决定

<bean id="mongoDBTemplate" class=" net.xuele.common.mongo.dao.multi.MongoDBTemplate">
    <property name="targetMongoTemplates">
        <map key-type="java.lang.String">
            <entry key="dao1" value-ref="mongoTemplate"/>
            <entry key="dao2" value-ref="mongoTemplate2"/>
        </map>
    </property>
    <property name="defaultTargetMongoTemplate" ref="mongoTemplate"/>
</bean>

<bean name="dbDecisionDaoAop" class="net.xuele.common.mongo.aop.DBDecisionDaoAop"/>
<!-- 配置AOP 切面 -->
<aop:config proxy-target-class="true">
    <!-- 定义切点函数 -->
    <aop:pointcut id="pointcut"
                  expression="execution(* net.xuele.circle.reposity.impl.*.*(..)))"/>
    <!-- 定义通知 order 定义优先级,值越小优先级越大-->
    <aop:aspect ref="dbDecisionDaoAop" order="0">
        <!-- 环绕通知 -->
        <aop:around method="doAround" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>
<!-- 框架提供的一个默认实现以供参考-->
<bean class="net.xuele.common.mongo.aop.handler.SpecifiedDecisionHandler" />

主要代码:

package net.xuele.common.mongo.dao.multi;

import org.springframework.data.mongodb.core.MongoTemplate;

/**
 * 可以切换数据源的mongoTemplate.
 *
 * @see MongodbTemplateContextHolder#setMongoDBTemplate(String)
 */
public class MongoDBTemplate extends AbstractMongoDBRoutingMongoTemplate {

    public MongoDBTemplate() {
    }

    public MongoTemplate getMongoTemplate() {
        return determineMongoTemplate();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return MongodbTemplateContextHolder.getMongoDBTemplate();
    }

}
package net.xuele.common.mongo.dao.multi;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;

/**
 * mongoTemplate路由
 */
public abstract class AbstractMongoDBRoutingMongoTemplate implements InitializingBean {

    /**
     * xml配置:mongoTemplate map(必选)
     */
    private Map<Object, Object> targetMongoTemplates;
    /**
     * xml配置:默认的mongoTemplate(必选)
     */
    private Object defaultTargetMongoTemplate;

    /**
     * 已经初始化的mongoTemplate,数据来源:targetMongoTemplates.
     */
    private Map<Object, MongoTemplate> resolvedMongoTemplates;
    /**
     * 默认的mongoTemplate,数据来源:defaultTargetMongoTemplate.
     */
    private MongoTemplate resolvedDefaultMongoTemplate;

    public void setTargetMongoTemplates(Map<Object, Object> targetMongoTemplates) {
        this.targetMongoTemplates = targetMongoTemplates;
    }

    public void setDefaultTargetMongoTemplate(Object defaultTargetMongoTemplate) {
        this.defaultTargetMongoTemplate = defaultTargetMongoTemplate;
    }

    @Override
    public void afterPropertiesSet() {
        if (this.targetMongoTemplates == null) {
            throw new IllegalArgumentException("Property 'targetMongoTemplates' is required");
        }
        this.resolvedMongoTemplates = new HashMap<>();
        for (Map.Entry<Object, Object> entry : this.targetMongoTemplates.entrySet()) {
            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();
}

package net.xuele.common.mongo.dao.multi;

import org.springframework.util.StringUtils;

import java.util.Map;

/**
 * 保存线程级别的mongoTemplate
 */
public class MongodbTemplateContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    /**
     * 设置当前使用的数据库
     *
     * @param mongodbTemplateType 对应xml配置targetMongoTemplates的key
     * @see AbstractMongoDBRoutingMongoTemplate#setTargetMongoTemplates(Map)
     */
    public static void setMongoDBTemplate(String mongodbTemplateType) {
        if (StringUtils.isEmpty(mongodbTemplateType)) {
            throw new NullPointerException("mongodbTemplateType is null");
        }
        contextHolder.set(mongodbTemplateType);
    }

    public static String getMongoDBTemplate() {
        return contextHolder.get();
    }

    /**
     * 当一次完整调用结束后,需要清除当前线程上下文.
     */
    public static void clearMongoDBTemplate() {
        contextHolder.remove();
    }
}

以上是获取MongoTemplate的逻辑,主要是参考网上的写法适当做修改,下面代码主要负责aop设置哪个template的逻辑。

package net.xuele.common.mongo.aop;

import net.xuele.common.mongo.aop.annotation.MultiMongo;
import net.xuele.common.mongo.aop.handler.DecisionHandler;
import net.xuele.common.mongo.dao.multi.MongodbTemplateContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 切换数据库的aop流程框架.
 *
 * @see DecisionHandler
 */
public class DBDecisionDaoAop implements ApplicationContextAware {

    private static Logger logger = LoggerFactory.getLogger(DBDecisionDaoAop.class);

    private ApplicationContext applicationContext;
    private List<DecisionHandler> handlerList;

    /**
     * 设置需要查询的某个表对应的数据库.
     *
     * @param joinPoint  切入点
     * @return 原方法返回的值
     * @throws Throwable 原方法抛出的异常
     */
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MultiMongo multiMongo = method.getAnnotation(MultiMongo.class);
        if (multiMongo == null) {
            return joinPoint.proceed();
        }
        DecisionInfo decisionInfo = new DecisionInfo();
        decisionInfo.setStrategy(multiMongo.strategy());
        decisionInfo.setSpecify(multiMongo.specify());
        decisionInfo.setCollectionName(multiMongo.collectionName());
        if (multiMongo.paramLocation() >= 0) {
            Object[] args = joinPoint.getArgs();
            decisionInfo.setSpecifyValue(args[multiMongo.paramLocation()]);
        }
        try {
            for (DecisionHandler decisionHandler : handlerList) {
                if (decisionHandler.support(decisionInfo)) {
                    MongodbTemplateContextHolder.setMongoDBTemplate(decisionHandler.handle(decisionInfo));
                    break;
                }
            }
            return joinPoint.proceed();
        } finally {
            MongodbTemplateContextHolder.clearMongoDBTemplate();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    public void postConstruct() {
        handlerList = new ArrayList<>(
                applicationContext.getBeansOfType(DecisionHandler.class).values());
    }
}
package net.xuele.common.mongo.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiMongo {
    /**
     * 策略需要的附属信息,比如直接指定某个数据库
     */
    String specify() default "";

    /**
     * 针对的表名.
     */
    String collectionName() default "";

    /**
     * 策略需要的参数值的位置,-1代表非必须
     */
    int paramLocation() default -1;

    /**
     * 使用的策略
     *
     * @return 策略id
     */
    String strategy();
}
package net.xuele.common.mongo.aop;

/**
 * 用哪个数据库的所有可能需要的条件.
 */
public class DecisionInfo {
    /**
     * 用哪个策略的key值
     */
    private String strategy;
    /**
     * 表名
     */
    private String collectionName;
    /**
     * 策略需要的附属信息,比如直接指定某个数据库
     */
    private String specify;
    /**
     * 策略需要的参数值
     */
    private Object specifyValue;

    public String getStrategy() {
        return strategy;
    }

    public void setStrategy(String strategy) {
        this.strategy = strategy;
    }

    public String getCollectionName() {
        return collectionName;
    }

    public void setCollectionName(String collectionName) {
        this.collectionName = collectionName;
    }

    public String getSpecify() {
        return specify;
    }

    public void setSpecify(String specify) {
        this.specify = specify;
    }

    public Object getSpecifyValue() {
        return specifyValue;
    }

    public void setSpecifyValue(Object specifyValue) {
        this.specifyValue = specifyValue;
    }
}
package net.xuele.common.mongo.aop.handler;

/**
 * 业务逻辑
 */
public interface DecisionHandler<T> {
    /**
     * 该处理器是否支持该参数的处理逻辑,支持,则后续调用handle方法.
     *
     * @param obj 传入的参数对象,比如DecisionInfo
     * @return 是否支持.
     */
    boolean support(T obj);

    /**
     * @param obj 数据
     * @return 数据库名字.
     */
    String handle(T obj);
}

aop会扫描所有实现该接口的bean作为策略的决策者。提供一个demo:

package net.xuele.common.mongo.aop.handler;

import net.xuele.common.mongo.aop.DecisionInfo;

/**
 * 指定数据库的策略
 */
public class SpecifiedDecisionHandler<T extends DecisionInfo> implements DecisionHandler<T> {


    public static final String SPECIFIED = "SPECIFIED";

    /**
     * 该处理器是否支持该参数的处理逻辑,支持,则后续调用handle方法.
     *
     * @param obj 传入的参数对象,比如DecisionInfo
     * @return 是否支持.
     */
    @Override
    public boolean support(DecisionInfo obj) {
        if (obj.getStrategy().equals(SPECIFIED)) {
            return true;
        }
        return false;
    }

    /**
     * @param obj 数据
     * @return 数据库名字.
     */
    @Override
    public String handle(DecisionInfo obj) {
        return obj.getSpecify();
    }
}

注解在实现类的方法上:

@MultiMongo(specify = "dao1",strategy= SpecifiedDecisionHandler.SPECIFIED)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值