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.....

posted @ 2022-12-23 17:57  aaacarrot  阅读(2400)  评论(0编辑  收藏  举报