mybatis拦截器 + 自定义注解 + 获取注解的属性
背景
mybatis拦截器 + 自定义注解——这种方式可以为我们解决很多事情,带来很多便利,但有时候会在自定义注解上配置一些属性,并且拦截器上要拿到这些属性的值。
这个时候,我们要怎样获取到这些值呢:
代码示例
- SM4MACFieldAnnotation 定义一个注解,用于字段进行数据防篡改的校验。然后要指定一些字段进行加密。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SM4MACFieldAnnotation {
/**
* 加密字段对应的字段
*/
String[] fieldKey() default {};
}
- 定义拦截器 SM4MACInterceptor
这个类上也还说明了,怎样获取mapper类的方法上的注解。
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@Intercepts({
@Signature(type = ParameterHandler.class,
method = "setParameters",
args = PreparedStatement.class),
})
public class SM4MACInterceptor implements Interceptor {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 重点!!! 获取Mapper方法上的注解
ParameterHandler resultSetHandler = (ParameterHandler) invocation.getTarget();
MetaObject metaResultSetHandler = MetaObject.forObject(resultSetHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
SM4MACFieldAnnotation annotation = getSM4MACFieldAnnotation(mappedStatement);
// 注解上配置的加密字段,拿到 !
String[] fields = annotation.fieldKey();
// 省略根据 fields 后做的加密,然后再做对象参数的反射......
// 执行
Object result = invocation.proceed();
// 返回结果
return result;
}
/**
* 获取方法上的SM4MACFieldAnnotation注解
*
* @param mappedStatement MappedStatement
* @return EncryptResultFieldAnnotation注解
*/
private SM4MACFieldAnnotation getSM4MACFieldAnnotation(MappedStatement mappedStatement) {
SM4MACFieldAnnotation annotation = null;
try {
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
final Method[] method = Class.forName(className).getMethods();
for (Method me : method) {
if (me.getName().equals(methodName) && me.isAnnotationPresent(SM4MACFieldAnnotation.class)) {
return me.getAnnotation(SM4MACFieldAnnotation.class);
}
}
} catch (Exception ex) {
log.error("", ex);
}
return annotation;
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
最后看一下,在mapper 方法上,添加注解的方法
要根据 bizId, name 这两个字段,进行加密,并做防篡改操作。
因为并不是每个方法都需要进行篡改操作,防篡改的字段也各不一样,所以必须要使用注解
@Mapper
public interface SM4MACProcessedMapper {
@SM4MACFieldAnnotation(fieldKey = {"bizId", "name"})
int insertSelective(ProcessedPo processedPo);
}
总结
以上,记录一下,在mapper 上怎样获利注解。
因为 mapper 接口上的注解和 通过 AOP获取注解的做法完全不一样。在AOP上拿到注解太简单了,但 mapper想拿到注解真的耗了不小的力气。
并且,还有一个点,要注意一下~!!!
mybatis 拦截的 @Signature 它的参数,
@Intercepts({
@Signature(type = ParameterHandler.class,
method = "setParameters",
args = PreparedStatement.class),
})
这里的 type 有对应的不同的类型,获取注解也不尽相同,比如这里是希望对参数进行操作,所以这里的type 是定义为了 ParameterHandler, 方法是 setParameters,
然后获取注解的时候,
ParameterHandler resultSetHandler = (ParameterHandler) invocation.getTarget(); 这个 getTarget 是和 type 对应的。
如果是对请求的结果进行操作,比如解密什么的,那这个签名,和这个 invocation.getTarget(); 也是不一样的,要注意些。
如果是要对返回的结果进行操作:
拦截器的注解是这样子写的:
@Intercepts({
@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class}
)
})
// 方法要这样写:
// 获取到返回结果
ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
MetaObject metaResultSetHandler = MetaObject.forObject(resultSetHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
......
知道这个就行,不同阶段,要使用不同的 Handler..... emmmm.....