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());
}
}
}}
推荐阅读
- 【故障公告】周五下午的一次突发故障
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- mybatisplus如何在xml的连表查询中使用queryWrapper
- mybatisplus|mybatisplus where QueryWrapper加括号嵌套查询方式
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- 我要我们在一起(二)
- 洱海不是海,,人群没有你
- 我的拖延症如何控制了我,又一次
- 跟身体谈恋爱