Spring实现数据库的读写分离

在mysql的主从架构,或者一主多从的架构中,需要做读写分离。除了使用中间件实现外,还可以使用SpringAOP在代码层面实现。下面是配置的过程以及代码。

1.使用Spring提供的AbstractRoutingDataSource实现多数据源的切换。

使用时需要继承这个类,实现其中的抽象方法。定义一个类RoutingDataSource继承AbstractRoutingDataSource

public class RoutingDataSource extends AbstractRoutingDataSource{
    private AtomicInteger count = new AtomicInteger(-1);
    //存储所有的从库key
    private List<Object> slaves = new ArrayList<Object>();
    protected Object determineCurrentLookupKey() {
         return RoutingDataSourceHandler.isMaster()? 
             RoutingDataSourceHandler.getDataSourceKey():getSlaveKey();
    }
    //轮询实现获取key
    private Object getSlaveKey(){
        Integer index = count.incrementAndGet() % slaves.size();
        if (count.get() > 9999)  // 以免超出Integer范围
            count.set(-1); // 还原
        return slaves.get(index); 
    }
    //重写此方法初始化这个类   
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        //通过反射获取父类私有属性
        Field field = ReflectionUtils.findField(AbstractRoutingDataSource.class, 
            "resolvedDataSources"); 
        try{
            Map<Object, DataSource> resolvedDataSources = 
                (Map<Object, DataSource>) field.get(this);
            for (Map.Entry<Object, DataSource> entry : resolvedDataSources.entrySet()) {
                if (DataSourceType.MASTER.equals(entry.getKey())) 
                    continue;
                slaves.add(entry.getKey());
            }
        }catch(Exception e){
        }   
    }
}
2.使用ThreadLocal解决线程安全问题。

首先定义一个枚举

public enum DataSourceType {
    MASTER,SLAVE
}

RoutingDataSourceHandler:

public class RoutingDataSourceHandler {
    private static final ThreadLocal<DataSourceType> typeContent = 
        new ThreadLocal<DataSourceType>();
    public static void putDataSourceKey(DataSourceType key) {
        typeContent.set(key);
    }
    public static DataSourceType getDataSourceKey() {
        return typeContent.get();
    }
    public static void markMaster() {
        putDataSourceKey(DataSourceType.MASTER);
    }
    public static void markSlave() {
        putDataSourceKey(DataSourceType.SLAVE);
    }

    public static boolean isMaster() {
        return DataSourceType.MASTER.equals(getDataSourceKey());
    }
}
3.定义一个注解,用在方法上标记方法为读或写。若不做注解,则按照方法名开头来决定使用读库或者写库。
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
public @interface ChoseDataSource {
    DataSourceType value() default DataSourceType.MASTER;
}
4.定义一个切面类DataSourceAspect
public class DataSourceAspect{
    private List<String> slaveMethodPattern = new ArrayList<String>();

    @SuppressWarnings("unchecked")
    public void setTxAdvice(TransactionInterceptor txAdvice)  throws Exception{
        Assert.notNull(txAdvice, "请注入事务策略");
        // 获取事务配置信息
        TransactionAttributeSource transactionAttributeSource 
            = txAdvice.getTransactionAttributeSource();
        if (!(transactionAttributeSource instanceof NameMatchTransactionAttributeSource)) 
            return;
        NameMatchTransactionAttributeSource matchTransactionAttributeSource = 
            (NameMatchTransactionAttributeSource) transactionAttributeSource;
        Field nameMapField = ReflectionUtils.findField(
            matchTransactionAttributeSource.getClass(), "nameMap");
        nameMapField.setAccessible(true); // 设置该字段可访问
    // 获取nameMap的值
        Map<String, TransactionAttribute> map = 
            (Map<String, TransactionAttribute>) nameMapField
            .get(matchTransactionAttributeSource);

    // 遍历nameMap
        for (Map.Entry<String, TransactionAttribute> entry : map.entrySet()) {
            if (!entry.getValue().isReadOnly()) 
                continue;
            slaveMethodPattern.add(entry.getKey());

        }
    public void before(JoinPoint point) {
        Object target = point.getTarget();
        String methodName = point.getSignature().getName();
        Class<?> clazz = target.getClass();
        Method method = ReflectionUtils.findMethod(clazz, methodName, (Class<?>) null);
        Assert.notNull(method);
        if (method.isAnnotationPresent(ChoseDataSource.class) || isSlave(methodName,slaveMethodPattern{
                            RoutingDataSourceHandler.putDataSourceKey(method.getAnnotation(ChoseDataSource.class).type());
        }

}
    private Boolean isSlave(String methodName,List<String> slaveMethodPattern) {
        for (String mappedName : slaveMethodPattern) {
            if (PatternMatchUtils.simpleMatch(methodName, mappedName)) {
                return true;
            }
        }
    return false;
    } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值