拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑
Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。
mybatis 自定义拦截器
1、实现Interceptor 接口,并添加拦截注解 @Intercepts
2、配置文件中添加拦截器
1、实现Interceptor接口,并添加拦截注解 @Intercepts
mybatis 拦截器默认可拦截的类型只有四种
Executor
:拦截执行器的方法。ParameterHandler
:拦截参数的处理。ResultHandler
:拦截结果集的处理。StatementHandler
:拦截Sql语法构建的处理。
对于我们的自定义拦截器必须使用 mybatis 提供的注解来指明我们要拦截的是四类中的哪一个类接口
具体规则如下:
a:Intercepts 拦截器:标识我的类是一个拦截器
b:Signature 署名:则是指明我们的拦截器需要拦截哪一个接口的哪一个方法
type
对应四类接口中的某一个,比如是 Executormethod
对应接口中的哪类方法,比如 Executor 的 update 方法args
对应接口中的哪一个方法,比如 Executor 中 query 因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法
@Intercepts({
@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),
@Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class})
})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Executor executor = (Executor)invocation.getTarget();
MappedStatement mappedStatement = ReflectUtil.getMethodField(executor, MappedStatement.class);
SqlSource sqlSource = ReflectUtil.getField(mappedStatement, SqlSource.class);
BoundSql boundSql = sqlSource.getBoundSql(args);
String sql = boundSql.getSql();
logger.info(sql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
String username = properties.getProperty("username");
String password = properties.getProperty("password");
// TODO: 2019/2/28 业务逻辑处理...
}
}
三个核心方法都加了详细的注释,而且结合案例需求说明问题
那么多文字不想行看,没关系有概括
总结:
1.在mybatis中可被拦截的类型有四种(按照拦截顺序)
Executor
:拦截执行器的方法。ParameterHandler
:拦截参数的处理。ResultHandler
:拦截结果集的处理。StatementHandler
:拦截Sql语法构建的处理。
2.各个参数的含义
@Intercepts
:标识该类是一个拦截器;@Signature
:指明自定义拦截器需要拦截哪一个类型,哪一个方法;
2.1 type:对应四种类型中的一种;
2.2 method:对应接口中的哪个方法;
2.3 args:对应哪一个方法参数类型(因为可能存在重载方法);
接下来我们看看 Plugin 类
package org.apache.ibatis.plugin;
class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
// 省略
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 省略
}
}
贴一段网上的通用解释吧:
- Plugin的wrap方法,它根据当前的Interceptor上面的注解定义哪些接口需要拦截,然后判断当前目标对象是否有实现对应需要拦截的接口,如果没有则返回目标对象本身,如果有则返回一个代理对象。
- 而这个代理对象的InvocationHandler正是一个Plugin。所以当目标对象在执行接口方法时,如果是通过代理对象执行的,则会调用对应InvocationHandler的invoke方法,也就是Plugin的invoke方法。
- 所以接着我们来看一下该invoke方法的内容。
- 这里invoke方法的逻辑是:如果当前执行的方法是定义好的需要拦截的方法,则把目标对象、要执行的方法以及方法参数封装成一个Invocation对象,再把封装好的Invocation作为参数传递给当前拦截器的intercept方法。
- 如果不需要拦截,则直接调用当前的方法。
- Invocation中定义了定义了一个proceed方法,其逻辑就是调用当前方法,所以如果在intercept中需要继续调用当前方法的话可以调用invocation的procced方法。
这就是Mybatis中实现Interceptor拦截的一个思想
2、在配置文件中添加拦截器
在springboot中要给mybatis加上这个拦截器,有三种方法,前两种方法在启动项目时不会自动调用自定义拦截器的setProperties方法。
拦截器顺序
1、不同拦截器顺序
Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
2、对于同一个类型的拦截器的不同对象拦截顺序:
在 mybatis 核心配置文件根据配置的位置,拦截顺序是 从上往下
(1)第一种
直接给自定义拦截器添加一个 @Component注解,当调用sql时结果如下,可以看到拦截器生效了,但是启动时候并没有自动调用setProperties方法。
@Component
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class MybatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//业务代码
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
(2)第二种
在配置类里添加拦截器,这种方法结果同上,也不会自动调用setProperties方法。
@Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(Configuration configuration) {
configuration.addInterceptor(new MybatisInterceptor());
}
};
}
}
(3)第三种
这种方法就是跟以前的配置方法类似,在yml配置文件中指定mybatis的xml配置文件,
注意:config-location属性和configuration属性不能同时指定
mybatis:
config-location: classpath:mybatis.xml
type-aliases-package: me.zingon.pagehelper.model
mapper-locations: classpath:mapper/*.xml
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="me.zingon.pacargle.model"/>
</typeAliases>
<plugins>
<plugin interceptor="me.zingon.pagehelper.interceptor.MyPageInterceptor">
<property name="dialect" value="oracle"/>
</plugin>
</plugins>
</configuration>
可以看到,在启动项目的时候setProperties被自动调用了
总结:前两种方法可以在初始化自定义拦截器的时候通过 @Value 注解直接初始化需要的参数。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。