摘要: 本篇文章的场景是做调度中心和监控中心时的需求, 后端使用 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);
来源: https://www.cnblogs.com/alterem/p/11286724.html