• mybatis拦截器(上)


    1. 拦截器注解

    1. mybatis自定义拦截器实现步骤:

    1. 实现org.apache.ibatis.plugin.Interceptor接口。
    2. 添加拦截器注解org.apache.ibatis.plugin.Intercepts
    3. 配置文件中添加拦截器。

    2. 在mybatis中可被拦截的类型有四种(按照拦截顺序):

    1. Executor:拦截执行器的方法。
    2. ParameterHandler:拦截参数的处理。
    3. ResultHandler:拦截结果集的处理。
    4. StatementHandler:拦截Sql语法构建的处理。

    1. 不同拦截类型执行顺序:

     
    com.galax.configuration.Aa#plugin打印拦截器对象顺序.png

    2. 多个插件拦截的顺序?

     
    image.png

    需要注意的是,因为拦截器Aa和拦截器Bb均是拦截的StatementHandler对象,所以拦截器B在此获取StatementHandler的时候,获取的是代理对象。

     
    拦截器对象的处理过程.png

    3. 多个插件plugin()和intercept()方法的执行顺序

    先执行每个插件的plugin方法,若是@Intercepts注解标明需要拦截该对象,那么生成类型对象的代理对象。(即使该插件需要拦截该类型对象,但是依旧会执行下一个插件的plugin方法)。知道执行完毕所有的plugin方法。在执行每个Intercept方法。

    3. 拦截器注解的作用:

    自定义拦截器必须使用mybatis提供的注解来声明我们要拦截的类型对象。

    Mybatis插件都要有Intercepts [in特赛婆斯]注解来指定要拦截哪个对象哪个方法。我们知道,Plugin.wrap方法会返回四大接口对象的代理对象,会拦截所有的方法。在代理对象执行对应方法的时候,会调用InvocationHandler处理器的invoke方法。

    4. 拦截器注解的规则:

    具体规则如下:

    @Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
    })
    
    1. @Intercepts:标识该类是一个拦截器;
    2. @Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;
      2.1 type:对应四种类型中的一种;
      2.2 method:对应接口中的哪类方法(因为可能存在重载方法);
      2.3 args:对应哪一个方法;

    5. 拦截器可拦截的方法:

    拦截的类拦截的方法
    Executor update, query, flushStatements, commit, rollback,getTransaction, close, isClosed
    ParameterHandler getParameterObject, setParameters
    StatementHandler prepare, parameterize, batch, update, query
    ResultSetHandler handleResultSets, handleOutputParameters

    2. 拦截器方法

    2.1 官方插件开发方式

    @Intercepts({@Signature(type = Executor.class, method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
    public class TestInterceptor implements Interceptor {
       public Object intercept(Invocation invocation) throws Throwable {
         Object target = invocation.getTarget(); //被代理对象
         Method method = invocation.getMethod(); //代理方法
         Object[] args = invocation.getArgs(); //方法参数
         // do something ...... 方法拦截前执行代码块
         Object result = invocation.proceed();
         // do something .......方法拦截后执行代码块
         return result;
       }
       public Object plugin(Object target) {
         return Plugin.wrap(target, this);
       }
    }
    

    2.2 拦截器的方法

    public interface Interceptor {   
       Object intercept(Invocation invocation) throws Throwable;       
       Object plugin(Object target);    
       void setProperties(Properties properties);
    }
    

    2.2.1 setProperties方法

    如果我们的拦截器需要一些变量对象,而且这个对象是支持可配置的。
    类似于Spring中的@Value("${}")从application.properties文件中获取。
    使用方法:

    <plugin interceptor="com.plugin.mybatis.MyInterceptor">
         <property name="username" value="xxx"/>
         <property name="password" value="xxx"/>
    </plugin>
    

    方法中获取参数:properties.getProperty("username");

    问题:但是为什么不直接使用@Value("${}") 获取变量?

    解答:因为mybatis框架本身就是一个可以独立使用的框架,没有像Spring这种做了很多的依赖注入。

    2.2.2 plugin方法

    这个方法的作用是就是让mybatis判断,是否要进行拦截,然后做出决定是否生成一个代理。

        @Override
        public Object plugin(Object target) {
            if (target instanceof StatementHandler) {
                return Plugin.wrap(target, this);
            }
            return target;
        }
    

    需要注意的是:每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。

    问题1:Plugin.wrap(target, this)方法的作用?

    解答:判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。

    故我们在实现plugin方法时,要判断一下目标类型,是本插件要拦截的对象时才执行Plugin.wrap方法,否则的话,直接返回目标本身。

    问题2:拦截器代理对象可能经过多层代理,如何获取到真实的拦截器对象?

        /**
         * <p>
         * 获得真正的处理对象,可能多层代理.
         * </p>
         */
        

    2.2.3 intercept(Invocation invocation)方法

    我们知道,mybatis只能拦截四种类型的对象。而intercept方法便是处理拦截到的对象。比如我们要拦截StatementHandler#query(Statement st,ResultHandler rh)方法,那么Invocation就是这个对象,Invocation中有三个参数。

    • target:StatementHandler;
    • method :query;
    • args[]:Statement st,ResultHandler rh

    org.apache.ibatis.reflection.SystemMetaObject#forObject:方便的获取对象中的值。

    案例:将参数拼接到sql语句。

    因为已经执行了ParameterHandler拦截器,故Statement对象已经是完全拼接好的SQL语句。

    附录

    1. 完整版拼接sql语句

    2. MappedStatement.class

    一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条sql语句。其属性为:

      //节点中的id属性加要命名空间
      private String id;
      //直接从节点属性中取
      private Integer fetchSize;
      //直接从节点属性中取
      private Integer timeout;
      private StatementType statementType;
      private ResultSetType resultSetType;
      //对应一条SQL语句
      private SqlSource sqlSource;
     
      //每条语句都对就一个缓存,如果有的话。
      private Cache cache;
      //这个已经过时了
      private ParameterMap parameterMap;
      private List<ResultMap> resultMaps;
      private boolean flushCacheRequired;
      private boolean useCache;
      private boolean resultOrdered;
      //SQL的类型,select/update/insert/detete
      private SqlCommandType sqlCommandType;
      private KeyGenerator keyGenerator;
      private String[] keyProperties;
      private String[] keyColumns;
      
      //是否有内映射
      private boolean hasNestedResultMaps;
      private String databaseId;
      private Log statementLog;
      private LanguageDriver lang;
      private String[] resultSets;


    作者:小胖学编程
    链接:https://www.jianshu.com/p/0a72bb1f6a21
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Alien Security (BFS+DFS)
    HDU 1495 喝可乐(暴力BFS)
    Tempter of the Bone(奇偶剪枝)の反面教材
    Fire Game (双起点bfs)
    Linux学习
    c# 实现IComparable、IComparer接口、Comparer类的详解
    DataTable 基本转换简单实例
    C#集合
    递归方法
    存储过程生成复杂的随机编号
  • 原文地址:https://www.cnblogs.com/dcmi/p/13228655.html
Copyright © 2020-2023  润新知