zoukankan      html  css  js  c++  java
  • Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的压力,在本篇文章中主要记录在Spring Boot中通过自定义注解结合AOP实现直接连接备库查询。

    一.通过AOP 自定义注解实现主库到备库的切换

    1.1 自定义注解

    自定义注解如下代码所示

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface SwitchDataBase {
        boolean switch2Backup() default false;
    }
    

    1.2 实现方法拦截器对自定义注解处理

    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * 处理走备库逻辑的注解
     */
    @Component
    public class SwitchDataBaseInterceptor implements MethodInterceptor {
    
        private final Logger log = LoggerFactory.getLogger(SwitchDataBaseInterceptor.class);
    
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();
            SwitchDataBase annotation = getAnnotation(method);
            if (annotation == null) {
                return invocation.proceed();
            }
            Object val = null;
            if(!ThreadLocalMap.containsKey(GroupDataSourceRouteHelper.DATASOURCE_INDEX)) {
                if (annotation.switch2Backup()) {
                    log.info("query back up DB, method: " + method.getName());
                    GroupDataSourceRouteHelper.executeByGroupDataSourceIndex(1, true);
                } else {
                    log.info("query primary DB, method: " + method.getName());
                    GroupDataSourceRouteHelper.executeByGroupDataSourceIndex(0, true);
                }
            }
            try {
                val = invocation.proceed();
            } catch (Exception e) {
                log.info(method.getDeclaringClass().getName() + "." +
                        invocation.getMethod().getName() + "方法调用失败,arguments:" +
                        Arrays.toString(invocation.getArguments()));
                throw new RuntimeException(e);
            } finally {
                GroupDataSourceRouteHelper.removeGroupDataSourceIndex();
            }
    
            return val;
        }
    
        /**
         * 找方法上面声明的注解
         */
        private SwitchDataBase getAnnotation(Method method) {
            if (method.isAnnotationPresent(SwitchDataBase.class)) {
                return method.getAnnotation(SwitchDataBase.class);
            }
            return null;
        }
    
    }
    

    1.3 配置OverallQueryConfiguration

    在Spring Boot中装配AOP Bean,实现扫描特定目录下的注解,实现切面变成形成通知处理。示例代码如下

    import com.wdk.wms.annotation.SwitchDataBaseInterceptor;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.support.DefaultPointcutAdvisor;
    import org.springframework.aop.support.JdkRegexpMethodPointcut;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class SwitchDataBaseConfiguration {
    
        @Bean(name = "overallQueryInterceptor")
        public SwitchDataBaseInterceptor overallQueryInterceptor() {
            return new SwitchDataBaseInterceptor();
        }
    
    
        //添加aop的pointcut
        @Bean(name = "jdkRegexpMethodPointcut")
        public JdkRegexpMethodPointcut jdkRegexpMethodPointcut() {
            JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
            jdkRegexpMethodPointcut.setPatterns("com.wdk.wms.mapper.*");
            return jdkRegexpMethodPointcut;
        }
    
        //设置默认的aop配置对应的是原来的<aop:advisor>
        @Bean
        public Advisor druidAdvisor() {
            DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
            defaultPointcutAdvisor.setPointcut(jdkRegexpMethodPointcut());
            defaultPointcutAdvisor.setAdvice(overallQueryInterceptor());
            return defaultPointcutAdvisor;
        }
    
    }
    

    1.4 如何使用注解从主库到备库的切换

    @SwitchDataBase(switch2Backup = true)
    List<ConsumerNoticeMsg> listByTemplateOver3(@Param("templates") List<Integer> templates);
    
  • 相关阅读:
    模拟ssh远程执行命令
    基于UDP协议的套接字编程
    TCP三次握手,四次挥手
    基于TCP协议的套接字编程
    osi七层协议
    Python之__class__.__module__,__class__.__name__
    异常处理
    单例模式
    类方法__setattr__,__delattr__,__getattr__
    反射(hasattr,getattr,delattr,setattr)
  • 原文地址:https://www.cnblogs.com/alterem/p/11286724.html
Copyright © 2011-2022 走看看