引言:

从天美的王者荣耀引发的测试数据展示到生产环境问题,及目前很多实际情况,需要测试在生产环境进行验证,如何把测试账号的数据从生产环境上过滤掉,但测试又可以在线上进行验证,这确实是个比较头疼的事情,我司也存在类似问题,我这边在进行预演,先说下预演情况,实际还是未真正解决(主要是希望优雅的解决)

方案一--AOP 切面

策略

采用 AOP 切面方式,把对应的 DAO 层文件做切面,把返回结果中过滤掉测试账号 ID

优势

可以指定特定 DAO 文件及特定方法进行处理

劣势

编写规则需要规范

预演代码

package com.finger.test.common.net;

import com.finger.test.pojo.UserAccountDO;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * @Des: mybatis结果集拦截器
 * @Auther: 飞狐
 * @Date: 2018/12/6
 */

public class SelectResultInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable{

        String className = methodInvocation.getClass().getName();
        String methodName = methodInvocation.getMethod().getName();

        Object result = methodInvocation.proceed();
        if(className.equals("UserAccountDOMapper")){
            if(methodName.startsWith("select")){

                UserAccountDO userAccountDO = (UserAccountDO) result;

                System.out.println(userAccountDO.getAccountNo());
            }
        }

        return result;
    }
}

拦截器 XML 配置

<bean id = "selectResultInterceptor" class="com.finger.test.common.net.SelectResultInterceptor" />
  <aop:config>
      <aop:pointcut id = "selectResultInterceptorCut" expression="execution(* com.finger.test.dao.*Mapper.*(..))" />
      <aop:advisor pointcut-ref="selectResultInterceptorCut" advice-ref="selectResultInterceptor"/>
  </aop:config>

方案二--mybatis 拦截器方案

策略

采用 mybatis 拦截器方案,针对 mybatis 底层 query 方法的返回结果进行拦截,过滤测试账号

优势

不区分 DAO 的命名规范,可随意定义

预演代码

package com.finger.test.common.net;

import com.finger.test.pojo.UserAccountDO;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.ArrayList;
import java.util.Properties;

/**
 * @Des:
 * @Auther: 飞狐
 * @Date: 2018/12/6
 */
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class }) })
public class InterceptorQuery implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable{

        MappedStatement statement = (MappedStatement)invocation.getArgs()[0];
//        statement.getStatementType().
//        System.out.println(statement.getSqlSource());


        Object result = invocation.proceed();

        if(result instanceof ArrayList){
            ArrayList resultList = (ArrayList) result;

            for(int i = 0; i < resultList.size(); i++){
                if(resultList.get(i) instanceof UserAccountDO){
                    UserAccountDO userAccountDO = (UserAccountDO) resultList.get(i);
                    if(userAccountDO.getUserId().equals(16904380000L)){
                        result = null;
                    }
                }
            }

        }

        return result;
    }

    public Object plugin(Object target){
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties){

    }
}

mybatis-config XML 配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- 全局配置参数需要时再设置 -->
    <settings>
        <!--&lt;!&ndash; 打开延迟加载 的开关 &ndash;&gt;-->
        <!--<setting name="lazyLoadingEnabled" value="true"/>-->
        <!-- 将积极加载改为消极加载即按需要加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 开启二级缓存  默认也是开启的-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <plugins>
        <plugin interceptor="com.finger.test.common.net.InterceptorQuery"></plugin>
    </plugins>

</configuration>

方案三 -- 自定义注解形式处理

说明

在对应的 DAO 层添加注解,在执行 sql 语句前,在注解层添加过滤测试账号的数据到 sql,生成新的 sql 语句进行执行

目前暂未预演此代码

方案四 -- 最不优雅的方式

说明

把测试账号统一写入到 redis,业务层把原账号 ID 输入到该方法中,返回过滤结果的 ID 列表

弊端

不用说,业务调用的地方太多,代码看起来非常冗余

综合考虑以上四种方式

还是无法解决,部分接口,可能是不想过滤测试账号,部分接口又是要过滤测试账号,又想代码优雅的情况

  1. 前面 2 种是一刀切方案,但好处是无感知;
  2. 第三种方案可能会让开发重新写一个一摸一样的 DAO 层方法,从而区别是否过滤测试账号,在已有的业务上增加,也是较大的工作量
  3. 第四种方案就不多说了,谁都看到是最笨的方案了

也想知道下其他同学有没好的方案建议


↙↙↙阅读原文可查看相关链接,并与作者交流