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.....
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性