1. 依赖引入
在当下绝多数的工程中通常采用 MyBatis
作为数据交互框架,同类此处在项目中导入下述依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
2. 注解介绍
以往的文章中已经详细介绍过拦截器的使用,这里仅简要概述一下的 @Intercepts
注解的参数配置。
@Intercepts
中提供了注解参数 @Signature
用于配置拦截目标信息,@Signature
注解的配置参数如下:
方法 | 作用 |
---|---|
type | 指定目标方法所在的接口或类。 |
method | 指定要拦截的目标方法的名称。 |
args | 指定拦截目标方法的参数类型。 |
假设存在目标类 TargetClass
,并包含了两个方法,具体内容如下:
public void TargetClass {
public void demo1(String s) {}
public void demo2(String s1, String s2) {}
}
若想要编写一个拦截器作用于上述 demo1()
方法,则 @Intercepts
注解的定义格式如下:
@Component
@Intercepts(
@Signature(
type = TargetClass.class,
method = "demo1",
args = { String.class }
)
)
public class TestInterceptor implements Interceptor { }
3. 拦截实现
基于上述描述,我们即可编辑拦截器实现 SQL 层面执行的跟踪编辑操作。
在 MyBatis
中 JDBC
的执行依托于 org.apache.ibatis.executor.Executor
接口,因此其对应的拦截器定义如下,其中定义了作用于 query()
方法查询。
@Component
@Intercepts(
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}
)
)
@SuppressWarnings("rawtypes")
public class QueryInterceptor implements Interceptor {
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object params = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
// Get sql detail
Executor executor = (Executor) invocation.getTarget();
BoundSql boundSql = ms.getBoundSql(params); ;
CacheKey cacheKey = executor.createCacheKey(ms, params, rowBounds, boundSql);
// Handle target sql
SqlCommandType sqlType = ms.getSqlCommandType();
if (sqlType == SqlCommandType.SELECT) {
System.out.println("=========> Query sql: " + boundSql.getSql());
return executor.query(ms, params, rowBounds, resultHandler, cacheKey, boundSql);
}
return invocation.proceed();
}
}
4. 工厂注入
上述步骤仅实现了拦截器,还需将其注入到 SqlSessionFactory
中才能在执行 SQL
时生效。
通过 Spring Boot
中的 ApplicationRunner
,即可实现在工程启动时注入拦截器到工厂类,代码如下:
@Component
public class SqlFactoryRunner implements ApplicationRunner {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@Override
public void run(ApplicationArguments args) throws Exception {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
// Inject interceptor to sql factory
sqlSessionFactory.getConfiguration().addInterceptor(new QueryInterceptor());
}
}
}