美文网首页
Spring注解切换MySQL主从

Spring注解切换MySQL主从

作者: 風暴之灵 | 来源:发表于2020-05-21 20:30 被阅读0次

    动态切换主从库
    首先看下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<DataSourceType> 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主从

          本文链接:https://www.haomeiwen.com/subject/hxztahtx.html