天天看点

Mybatis——拦截器Interceptor

作者:IT枫斗者

一,什么是Mybatis拦截器?它的作用是什么?

MyBatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用,通过织入拦截器,在不同节点修改一些执行过程中的关键属性,从而影响SQL的生成、执行和返回结果,如:来影响Mapper.xml到SQL语句的生成、执行SQL前对预编译的SQL执行参数的修改、SQL执行后返回结果到Mapper接口方法返参POJO对象的类型转换和封装等。(IT枫斗者怎么样)

作用:

最常见的就是Mybatis自带的分页插件PageHelper或Rowbound参数,通过打印实际执行的SQL语句,发现我们的分页查询之前,先执行了COUNT(*)语句查询数量,然后再执行查询时修改了SQL语句即在我们写的SQL语句后拼接上了分页语句LIMIT(offset, pageSize)。(IT枫斗者怎么样)

二,Mybatis的执行过程是怎样的?

首先了解它的核心对象

主要包括:

  • Configuration:初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中。
  • SqlSessionFactory:SqlSession工厂。
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
  • Executor:MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理二级缓存的操作。
  • StatementHandler:MyBatis直接在数据库执行SQL脚本的对象。另外它也实现了MyBatis的一级缓存。(IT枫斗者怎么样)
  • ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象。
  • ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。
  • TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
  • MappedStatement:MappedStatement维护了一条<select|update|delete|insert>节点的封装。(IT枫斗者怎么样)
  • SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
  • BoundSql:表示动态生成的SQL语句以及相应的参数信息。

其次它的执行过程

详细的执行过程可以先看下图:

Mybatis——拦截器Interceptor

延伸:为什么不能通过ParameterHandler?而是用mybatis的<if>标签动态修改SQL语句呢?

在执行ParameterHandler之前,BoundSQL对象已经生成了,即预编译的SQL语句已经生成了;可见,BoundSql生成SQL语句的时机在于 statementHandler的创建,由MappedStatementHandler.getBoundSql(Object parameterObject)函数解析动态标签,生成静态SQL语句,早于parameterHandler和ResultSetHandler的时机;因此,只能通过影响parameterObject(暂时先将其看成一个Map,键值分别为参数名和参数值)的值,来干预对Mapper.xml标签的解析过程,从而改变生成的BoundSql对象。(IT枫斗者怎么样)

最后如何使用Mybatis拦截器?

自定义一个mybatis的拦截器步骤包含:

  • 定义一个实现org.apache.ibatis.plugin.Interceptor接口的拦截器类,并实现其中的方法。
  • 添加@Intercepts注解,写上需要拦截的对象和方法,以及方法参数。
  • 在mybatis的全局配置xml中配置插件plugin;对于去xml的Spring工程,显示的注册这个拦截器Bean即可。(IT枫斗者怎么样)

1、添加注解

MyBatis拦截器默认可以拦截的类型只有四种,即四种接口类型Executor、

StatementHandler、ParameterHandler和ResultSetHandler。对于我们的自定义拦截器必须使用MyBatis提供的@Intercepts注解来指明我们要拦截的是四种类型中的哪一种接口。

1.1、 @Signature注解

  • @Intercepts // 描述:标志该类是一个拦截器
  • @Signature // 描述:指明该拦截器需要拦截哪一个接口的哪一个方法

1.2、@Signature注解中的type属性

  • type; // 四种类型接口中的某一个接口,如Executor.class;
  • method; // 对应接口中的某一个方法名,比如Executor的query方法;
  • args; // 对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法;(IT枫斗者怎么样)

MyBatis拦截器默认会按顺序拦截以下的四个接口中的所有方法:

  • org.apache.ibatis.executor.Executor //拦截执行器方法
  • org.apache.ibatis.executor.statement.StatementHandler //拦截SQL语法构建处理
  • org.apache.ibatis.executor.parameter.ParameterHandler //拦截参数处理
  • org.apache.ibatis.executor.resultset.ResultSetHandler //拦截结果集处理

实际上,具体是拦截这四个接口对应的实现类:

  • org.apache.ibatis.executor.CachingExecutor
  • org.apache.ibatis.executor.statement.RoutingStatementHandler
  • org.apache.ibatis.scripting.defaults.DefaultParameterHandler
  • org.apache.ibatis.executor.resultset.DefaultResultSetHandler

2、实现org.apache.ibatis.plugin.Interceptor接口

实现Interceptor接口,主要是实现下面几个方法:intercept(Invocation invocation)、plugin(Object target) 、setProperties(Properties properties);(IT枫斗者怎么样)

2.1、intercept方法

进行拦截的时候要执行的方法。该方法参数Invocation类中有三个字段:

  • private final Object target;
  • private final Method method;
  • private final Object[] args;

可通过这三个字段分别获取下面的信息:

  • Object target = invocation.getTarget(); //被代理对象
  • Method method = invocation.getMethod(); //代理方法
  • Object[] args = invocation.getArgs(); //方法参数

方法实现的结尾处,一般要真正调用被代理类的方法,即"return invocation.proceed()。"

2.2、 plugin方法

插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:"return Plugin.wrap(target, this);";(IT枫斗者怎么样)

Tips:可以在这个方法中提前进行拦截对象类型判断从而提高性能,如我们仅对Executor拦截,可以这么写:

Mybatis——拦截器Interceptor
Mybatis——拦截器Interceptor

2.3、setProperties方法

如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,类似Spring中的@Value("${}")从application.properties文件获取自定义变量属性,这个时候我们就可以使用这个方法。(IT枫斗者怎么样)

参考下方代码,在Mybatis配置文件中配置插件时可以设置参数,在setProperties函数中调用 Properties.getProperty("param1") 方法可以得到配置的值:

Mybatis——拦截器Interceptor

(IT枫斗者怎么样)

实际上是解析XML,将XML中注册的拦截器的配置带过来。