Spring注解切换MySQL主从

动态切换主从库
首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean

既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:

public Connection getConnection(String username, String password) throws SQLException {  
    return determineTargetDataSource().getConnection(username, password);  
}

展开determineTargetDataSource方法

protected DataSource determineTargetDataSource() {  
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");  
    Object lookupKey = determineCurrentLookupKey();  
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);  
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {  
        dataSource = this.resolvedDefaultDataSource;  
    }  
    if (dataSource == null) {  
        throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");  
    }  
    return dataSource;  
}

这里用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到,则用默认的resolvedDefaultDataSource。

这里我们应创建自己的类,继承AbstractRoutingDataSource类,实现determineCurrentLookupKey方法

public class DataSourceRouting extends AbstractRoutingDataSource {

    public static final ThreadLocal DATA_SOURCE_TYPE = new ThreadLocal<>();

    @Override
    protected Object determineCurrentLookupKey() {
        return DATA_SOURCE_TYPE.get();
    }
}

数据源的名称常量类

public enum DataSourceType {
    MASTER,
    SLAVE
}

这里我们切换数据源的方法是通过在切面,使service层使用了@Transaction注解的方法切换成主库,其他的方法读取从库,在方法运行结束后再切换成从库

@Aspect
@Slf4j
public class DataSourceAspect {

//    @Pointcut注解内容为匹配*..service.impl路径下以ServiceImpl结尾的文件中所有有参数列表的函数
    @Pointcut("execution(* *..service.impl.*ServiceImpl.*(..))")
    public void pointCut() {
        // Do nothing just for pointCut.
    }

    @Before(value = "pointCut()")
    public void before(JoinPoint point) {
        Object target = point.getTarget();
        Class clazz = target.getClass();
        String method = point.getSignature().getName();
        log.debug(clazz.getName() + "." + method + "()");
        Class[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            Method m = clazz.getMethod(method, parameterTypes);
            DataSourceRouting.DATA_SOURCE_TYPE.set(DataSourceType.SLAVE);
            if (m != null) {
                if (m.isAnnotationPresent(Transactional.class)) {
                    Transactional transactional = m.getAnnotation(Transactional.class);
                    setDataSource(transactional);
                } else if (clazz.isAnnotationPresent(Transactional.class)) {
                    Transactional transactional = clazz.getAnnotation(Transactional.class);
                    setDataSource(transactional);
                }
            }
            log.info("user dataSource:" + DataSourceRouting.DATA_SOURCE_TYPE.get());
        } catch (NoSuchMethodException e) {
            log.error("", e);
        }
    }

//方法期间使用主库之后切换回从库
    @After(value = "pointCut()")
    public void after() {
        log.debug("remove dataSource:" + DataSourceRouting.DATA_SOURCE_TYPE.get());
        DataSourceRouting.DATA_SOURCE_TYPE.remove();
    }

    private void setDataSource(Transactional transactional) {
        DataSourceRouting.DATA_SOURCE_TYPE.set((transactional.propagation() == Propagation.NOT_SUPPORTED
                || transactional.propagation() == Propagation.NEVER || transactional.readOnly()) ?
                DataSourceType.SLAVE : DataSourceType.MASTER);
    }
}

你可能感兴趣的:(Spring注解切换MySQL主从)