Spring Boot中自定義註解+AOP實現主備庫切換

alterem發表於2019-08-02

摘要: 本篇文章的場景是做排程中心和監控中心時的需求,後端使用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);複製程式碼

相關文章