MyBatis插件一次开发的总结

【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="https://www.it610.com/article/mobilephone",keyType = "test") @Param("mobilephone") String mobilephone); Long getUserIdByMobiles(@Encrypt(value="https://www.it610.com/article/mobilephone",keyType = "test") @Param("mobilephone")List mobilephone); Long getUserIdByMobiles2(@Encrypt(value="https://www.it610.com/article/mobilephone",keyType = "test") @Param("mobilephone")Set 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 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 = https://www.it610.com/article/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 SetSet T-> List List * * @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类型 * * @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 = https://www.it610.com/article/field.get(obj); field.set(obj,"encode:" + value); handerFlag.set(true); } catch (IllegalAccessException e) { logger.error("IllegalAccessException", e); } } }/** * 处理Set * * @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 INITIALIZE_ENCRY_PARAM_MAP = new ConcurrentHashMap(64); public static ConcurrentMap 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()); } } }}



    推荐阅读