MyBatis插件一次开发的总结

最近项目有个功能,对指定的参数进行加密,考虑使用AOP还是插件方式开发。我进行插件方式开发的研究,虽然最终采用了AOP的方式,但是我还是花时间记录一下,以后用到可以方面参考。因为是demo版本的代码,代码质量可能有所欠缺,哈哈。

1.先定义一个需要加密的注解,该注解可以作用于参数上、和bean的属性上。keyType可以忽略,业务自有的。

/**
 * @author krauser
 * @date Create in 下午5:42 2018/6/6
 * @Description
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface Encrypt {

    String value() default "";

    String keyType() default "";

}
2.具体的使用就是在查询参数上加上Encrypt注解,如果查询参数是vo、do需要在属性上再加上该注解
public interface UserMapper {
    /**
     * 通过手机号查询userId
     * @param mobilephone 手机号
     * @return userId
     */
    Long getUserIdByMobile(@Encrypt(value="mobilephone",keyType = "test") @Param("mobilephone") String mobilephone);

    Long getUserIdByMobiles(@Encrypt(value="mobilephone",keyType = "test")
                            @Param("mobilephone")List<String> mobilephone);

    Long getUserIdByMobiles2(@Encrypt(value="mobilephone",keyType = "test")
                            @Param("mobilephone")Set<String> mobilephone);
}
 @Column(name = "units_phone")
 @Encrypt("unitsPhone")
 private String unitsPhone;

3.具体的实现,因为业务的关系,最后采用了AOP的方式,但是经过这次的开发,对MyBatis的执行过程有了一个深层次的认识。其实代码还有很多优化的地方,比如可以自动获取参数,但是需要编译的时候加上对应的参数,否则获得是arg0这种数据。对于List的处理其实现在还是很懵,有大牛知道一下,parameterMapping的加载过程中,可以教我一下。

/**
 * @author krauser
 * @date Create in 下午5:32 2018/6/6
 * @Description List String Set Array Collection
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
})
public class EncryptSensitiveInterceptor implements Interceptor {

    public static final Logger logger = LoggerFactory.getLogger(EncryptSensitiveInterceptor.class);

    public static final String STRING_NAME = "String";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ConcurrentMap<String, String> initializeParameterEncryptMap = InitializeParameter.getInitializeParameterEncryptMap();
        if (initializeParameterEncryptMap == null || initializeParameterEncryptMap.isEmpty()) {
            logger.warn("initializeParameterEncryptMap is empty");
            return invocation.proceed();
        }
        // 获取方法所有的参数
        final Object[] args = invocation.getArgs();
        try {
            // 获取MappedStatement的对象
            MappedStatement originalMappedStatement = (MappedStatement) args[0];
            // SQL参数
            Object parametersObject = args[1];
            // 判断SQL指令的类型
            SqlCommandType commondType = originalMappedStatement.getSqlCommandType();
            // SQL的包装对象
            BoundSql boundSql = originalMappedStatement.getBoundSql(parametersObject);
            // 判断指令标签
            if (commondType.compareTo(SqlCommandType.SELECT) == 0) {
                String keyPath = originalMappedStatement.getId();
                String params = initializeParameterEncryptMap.get(keyPath);
                String[] paramsArr = StringUtils.split(params, ";");
                if (paramsArr == null) {
                    logger.info("params is null,noting need to do");
                    return invocation.proceed();
                }
                parametersObjectHandler(originalMappedStatement, boundSql, parametersObject, paramsArr, args);
            }
        } catch (Exception e) {
            logger.error("参数加解密操作异常", e);
        }
        return invocation.proceed();
    }


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

    @Override
    public void setProperties(Properties properties) {
        // do noting
    }


    /**
     * 替换原生的MappedStatement
     *
     * @param ms
     * @param newSqlSource
     * @return
     */
    private MappedStatement replaceMappedStatement(MappedStatement ms,
                                                   SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(),
                ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
            builder.keyProperty(ms.getKeyProperties()[0]);
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }

    /**
     * 实现SqlSource,获取BoundSql
     */
    static class CustomSqlSource implements SqlSource {

        private BoundSql boundSql;

        public CustomSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        @Override
        public BoundSql getBoundSql(Object o) {
            return boundSql;
        }
    }

    /**
     * MappedStatement Mybatis与数据交互的对象
     * BoundSql SQL包装类
     * Object   参数Object
     * String[] 需要加密的参数名称数组
     * Object[] Executor query 方法的所有参数
     *
     * @param originalMappedStatement
     * @param boundSql
     * @param parametersObject
     * @param paramsArr
     * @param args
     */
    private void parametersObjectHandler(MappedStatement originalMappedStatement, BoundSql boundSql, Object parametersObject, String[] paramsArr, Object[] args) {
        AtomicBoolean handerFlag = new AtomicBoolean(false);
        Map parametersMap;
        if (parametersObject != null && (parametersObject instanceof Map)) {
            parametersMap = (Map) parametersObject;
            Stream.of(paramsArr).forEach(param -> {
                Object paramValue = parametersMap.get(param);
                if (paramValue != null) {
                    paramValueHandler(paramValue, param, parametersMap, handerFlag, boundSql);
                }
            });
            if (handerFlag.get()) {
                BoundSql newBoundSql = new BoundSql(
                        originalMappedStatement.getConfiguration(),
                        boundSql.getSql(),
                        boundSql.getParameterMappings(),
                        parametersMap);
                // 赋值额外的参数
                for (ParameterMapping mapping : boundSql.getParameterMappings()) {
                    String prop = mapping.getProperty();
                    if (boundSql.hasAdditionalParameter(prop)) {
                        newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
                    }
                }
                args[0] = replaceMappedStatement(originalMappedStatement, new CustomSqlSource(newBoundSql));
            }
        }
    }

    /**
     * 支持Sting Set<T>  Set<String> T  -> List<T> List<String>
     *
     * @param paramValue
     * @param param
     * @param parametersMap
     * @param handerFlag
     * @return
     */
    private void paramValueHandler(Object paramValue, String param, Map parametersMap, AtomicBoolean handerFlag, BoundSql boundSql) {
        if (paramValue instanceof String) {
            paramValueStringHandler(paramValue, handerFlag, param, parametersMap);
        } else if (paramValue instanceof List) {
            paramValueListHandler(paramValue, handerFlag, parametersMap, boundSql, param);
        } else if (paramValue instanceof Set) {
            paramValueSetHandler(paramValue, handerFlag, parametersMap, boundSql, param);
        } else {
            paramValueNorClassHandler(paramValue, handerFlag);
        }
    }

    /**
     * 处理String类型的数据
     *
     * @param paramValue
     * @param handerFlag
     * @param param
     * @param parametersMap
     */
    private void paramValueStringHandler(Object paramValue, AtomicBoolean handerFlag, String param, Map parametersMap) {
        parametersMap.put(param, "encode:" + paramValue);
        handerFlag.set(true);
    }

    /**
     * 类字段有参数的
     *
     * @param paramValue
     * @param handerFlag
     */
    private void paramValueNorClassHandler(Object paramValue, AtomicBoolean handerFlag) {
        Field[] fields = paramValue.getClass().getDeclaredFields();
        for (int i = 0; fields != null && i < fields.length; i++) {
            Field field = fields[i];
            filedHandler(field, handerFlag, paramValue);
        }
    }

    /**
     * 处理List<String>类型
     *
     * @param paramValue
     * @param handerFlag
     * @param parametersMap
     * @param boundSql
     * @param param
     */
    private void paramValueListHandler(Object paramValue, AtomicBoolean handerFlag, Map parametersMap, BoundSql boundSql, String param) {
        List paramValueList = (ArrayList) paramValue;
        if (CollectionUtils.isEmpty(paramValueList)) {
            return;
        }
        Class<?> paramValueClass = paramValueList.get(0).getClass();
        if (STRING_NAME.equalsIgnoreCase(paramValueClass.getSimpleName())) {
            for (int i = 0; i < paramValueList.size(); i++) {
                paramValueList.set(i, "encode:" + paramValueList.get(i));
            }
            parametersMap.put(param, paramValueList);
            for (ParameterMapping mapping : boundSql.getParameterMappings()) {
                String prop = mapping.getProperty();
                if (boundSql.hasAdditionalParameter(prop)) {
                    boundSql.setAdditionalParameter(prop, "encode:" + boundSql.getAdditionalParameter(prop));
                }
            }
            handerFlag.set(true);
        } else {
            paramValueListNorClassHandler(paramValueList, handerFlag);
        }
    }

    /**
     * 集合的范型是类
     *
     * @param paramValueList
     * @param handerFlag
     */
    private void paramValueListNorClassHandler(List paramValueList, AtomicBoolean handerFlag) {
        for (int j = 0; j < paramValueList.size(); j++) {
            Field[] fields = paramValueList.get(j).getClass().getDeclaredFields();
            for (int i = 0; fields != null && i < fields.length; i++) {
                Field field = fields[i];
                filedHandler(field, handerFlag, paramValueList.get(j));
            }
        }
    }

    /**
     * Set的范型是类
     *
     * @param paramValueSet
     * @param handerFlag
     */
    private void paramValueSetNorClassHandler(Set paramValueSet, AtomicBoolean handerFlag) {
        paramValueSet.stream().forEach(subSetValue -> {
            Field[] fields = subSetValue.getClass().getDeclaredFields();
            for (int i = 0; fields != null && i < fields.length; i++) {
                Field field = fields[i];
                filedHandler(field, handerFlag, subSetValue);
            }
        });
    }

    /**
     * 字段处理器
     *
     * @param field
     * @param handerFlag
     * @param obj
     */
    private void filedHandler(Field field, AtomicBoolean handerFlag, Object obj) {
        field.setAccessible(true);
        Encrypt annotation = field.getAnnotation(Encrypt.class);
        if (annotation != null) {
            Object value;
            try {
                value = field.get(obj);
                field.set(obj, "encode:" + value);
                handerFlag.set(true);
            } catch (IllegalAccessException e) {
                logger.error("IllegalAccessException", e);
            }
        }
    }


    /**
     * 处理Set<String>
     *
     * @param paramValue
     * @param handerFlag
     * @param parametersMap
     * @param boundSql
     * @param param
     */
    private void paramValueSetHandler(Object paramValue, AtomicBoolean handerFlag, Map parametersMap, BoundSql boundSql, String param) {
        Set paramValueSet = (Set) paramValue;
        if (CollectionUtils.isEmpty(paramValueSet)) {
            return;
        }
        Class<?> paramValueClass = null;
        Optional firstEle = paramValueSet.stream().findFirst();
        if (firstEle.isPresent()) {
            paramValueClass = firstEle.get().getClass();
        }
        if (paramValueClass != null && STRING_NAME.equalsIgnoreCase(paramValueClass.getSimpleName())) {
            parametersMap.put(param, paramValueSet);
            for (ParameterMapping mapping : boundSql.getParameterMappings()) {
                String prop = mapping.getProperty();
                if (boundSql.hasAdditionalParameter(prop)) {
                    boundSql.setAdditionalParameter(prop, "encode:" + boundSql.getAdditionalParameter(prop));
                }
                handerFlag.set(true);
            }
        } else if (paramValueClass != null) {
            paramValueSetNorClassHandler(paramValueSet, handerFlag);
        }
    }

}

4.补充一下initMap的初始化代码,非重点

@Component
public class InitializeParameter implements InitializingBean, ApplicationContextAware {

    public static final Logger logger = LoggerFactory.getLogger(InitializeParameter.class);

    private ApplicationContext applicationContext;

    private static final String MAPPER = "Mapper";

    /**
     * key为方法路径value 为方法需要加密的参数
     */
    private static final ConcurrentMap<String, String> INITIALIZE_ENCRY_PARAM_MAP = new ConcurrentHashMap(64);

    public static ConcurrentMap<String, String> getInitializeParameterEncryptMap() {
        return INITIALIZE_ENCRY_PARAM_MAP;
    }

    @PostConstruct
    public void initializeParameter() {
        String[] names = applicationContext.getBeanDefinitionNames();
        if (names == null) {
            logger.warn("BeanDefinitionNames is null");
            return;
        }
        beanDefinitionNamesHandler(names);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // do nothing
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * beanName处理器
     *
     * @param names
     */
    private void beanDefinitionNamesHandler(String[] names) {
        Stream.of(names).forEach(name -> {
            if (name.endsWith(MAPPER)) {
                Class<?> type = applicationContext.getType(name);
                if (type == null) {
                    logger.warn("class type is null");
                    return;
                }
                classHandler(type);
            }
        });
    }

    /**
     * 类处理器
     *
     * @param type
     */
    private void classHandler(Class<?> type) {
        for (Method method : type.getDeclaredMethods()) {
            Parameter[] parameters = method.getParameters();
            for (int j = 0; parameters != null && j < parameters.length; j++) {
                Parameter parameter = parameters[j];
                if (parameter == null) {
                    continue;
                }
                String path = type.getName() + "." + method.getName();
                encryptAnnotationHandler(parameter.getAnnotation(Encrypt.class), path);
            }
        }
    }

    /**
     * 注解处理器
     *
     * @param annotations
     * @param path
     */
    private void encryptAnnotationHandler(Encrypt annotations, String path) {
        if (annotations != null && StringUtils.isNotBlank(annotations.value())) {
            if (INITIALIZE_ENCRY_PARAM_MAP.get(path) != null) {
                INITIALIZE_ENCRY_PARAM_MAP.put(path, INITIALIZE_ENCRY_PARAM_MAP.get(path) + ";" + annotations.value());
            } else {
                INITIALIZE_ENCRY_PARAM_MAP.put(path, annotations.value());
            }
        }
    }

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值