Spring SQL 拦截器实现


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 层面执行的跟踪编辑操作。

MyBatisJDBC 的执行依托于 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());
        }
    }
}

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录