MyBatis拦截器动态注册

1. 目标

使用 MyBatis 拦截器,可以实现对 Java 应用的数据库操作进行一些灵活的控制,为了便于使用,可能需要对其进行动态注册,以下进行分析

相关内容:

Druid过滤器动态注册

2. MyBatis 拦截器动态注册

在注册 MyBatis 拦截器时,可能需要使其在所有的 MyBatis 拦截器中靠前执行,也可能需要靠后执行,需要按照不同的方式实现

2.1. MyBatis 拦截器的注册与执行顺序

在 org.apache.ibatis.session.Configuration:newExecutor() 方法中,调用了 org.apache.ibatis.plugin.InterceptorChain:pluginAll() 方法,代码如下:

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

以上 interceptors 对应 InterceptorChain 类中的 List interceptors 字段

以上 pluginAll() 方法中的 “target = interceptor.plugin(target);” 处理,会使 interceptors 列表中排在后面的 MyBatis 拦截器 interceptor 先执行

InterceptorChain 类只有一个增加拦截器的方法 addInterceptor(),只能将指定的拦截器增加到 interceptors 列表最后;getInterceptors() 方法返回的拦截器列表是不可修改的

因此为了使组件中的 MyBatis 拦截器执行顺序靠后,只能使其注册顺序靠前

2.2. 使 MyBatis 拦截器靠后执行的注册方式

为了使某个 MyBatis 拦截器执行顺序靠后,需要使其注册顺序靠前

假如某个 MyBatis 拦截器通过类 A 的构造函数获取类型为 “Map<String, SqlSessionFactory> sqlSessionFactoryMap” 的参数,并在构造函数中注册了 MyBatis 拦截器 B,为了使自己的 MyBatis 拦截器执行顺序晚于以上 MyBatis 拦截器 B,则需要比以上拦截器更早注册

根据 Spring Bean 的创建顺序,会先创建被依赖的 SqlSessionFactory 类对应 Bean,再创建以上类 A 对应的 Bean

在 SqlSessionFactory 类对应 Bean 创建过程中,会调用 BeanPostProcessor 接口实现类进行 Bean 初始化前及初始化后的方法,以上方法执行都早于类 A 构造函数被调用,可以满足要求

2.2.1. MyBatis 拦截器与 SQL 会话工厂

注册 MyBatis 拦截器时,需要调用 org.apache.ibatis.session.Configuration 类的 addInterceptor() 方法

通过 org.apache.ibatis.session.SqlSessionFactory 接口的 getConfiguration() 方法可以获得对应的 Configuration 对象

在 org.mybatis.spring.SqlSessionFactoryBean 类中有字段 SqlSessionFactory sqlSessionFactory

在 SqlSessionFactoryBean 类的 afterPropertiesSet() 方法中,调用 buildSqlSessionFactory() 方法创建了 SqlSessionFactory 实现类,并赋值到 sqlSessionFactory 字段

在 SqlSessionFactoryBean 类的 getObject() 方法中,会返回 sqlSessionFactory 字段,假如为 null 会先调用 afterPropertiesSet() 创建,getObject() 方法代码如下:

  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

2.2.2. 不可行方式 - 在 SqlSessionFactory 初始化前处理

在 BeanPostProcessor 实现类的 postProcessBeforeInitialization() 方法中,无法接收到 org.apache.ibatis.session.SqlSessionFactory 实现类的初始化前操作,因此不能通过这种方式注册 MyBatis 拦截器

2.2.3. 不可行方式 - 在 SqlSessionFactoryBean 初始化前处理

假如在 SqlSessionFactoryBean 初始化前,即 BeanPostProcessor.postProcessBeforeInitialization() 方法中,接收到 org.mybatis.spring.SqlSessionFactoryBean 类的初始化前操作时进行处理

通过 SqlSessionFactoryBean.getObject() 方法获得 SqlSessionFactory 实现类时,此时 SqlSessionFactoryBean 对象的 sqlSessionFactory 字段为 null,会通过 afterPropertiesSet() 方法为 sqlSessionFactory 字段赋值

以上 afterPropertiesSet() 方法定义在 org.springframework.beans.factory.InitializingBean 接口中,SqlSessionFactoryBean 类有实现该接口

由于 Bean 实现的 InitializingBean.afterPropertiesSet() 方法在 BeanPostProcessor.postProcessBeforeInitialization() 方法之后执行,SqlSessionFactoryBean 类的 afterPropertiesSet() 会再次执行,以上方法及 buildSqlSessionFactory() 方法中的操作都没有判断 sqlSessionFactory 字段是否为 null,会对 sqlSessionFactory 字段重新赋值

因此项目中实际使用的 SqlSessionFactory 实现类是 SqlSessionFactoryBean.afterPropertiesSet() 方法执行时创建的,在更早的步骤 BeanPostProcessor.postProcessBeforeInitialization() 方法中通过 SqlSessionFactoryBean.getObject() 方法获得的 SqlSessionFactory 实现类后续会被覆盖掉,在 SqlSessionFactoryBean 初始化前处理操作没有作用

2.2.4. 可行方式 - 在 SqlSessionFactory 初始化后处理

在 BeanPostProcessor 实现类的 postProcessAfterInitialization() 方法中,接收到 SqlSessionFactory 实现类的初始化后操作

调用 SqlSessionFactory.getConfiguration() 方法获得 Configuration 对象

调用 Configuration 对象的 addInterceptor() 方法增加 MyBatis 拦截器

2.2.5. 可行方式 - 在 SqlSessionFactoryBean 初始化后处理

在 BeanPostProcessor 实现类的 postProcessAfterInitialization() 方法中,接收到 SqlSessionFactoryBean 类的初始化后操作

调用 SqlSessionFactoryBean.getObject() 方法中,获得 SqlSessionFactory 对象

后续操作同上

2.2.6. 选择的实现方式

选择在 SqlSessionFactory 初始化后处理步骤,即 BeanPostProcessor 实现类的 postProcessAfterInitialization() 方法中,接收到 SqlSessionFactory 实现类的初始化后进行处理,将组件中的 MyBatis 拦截器 MyBatisConcurrencyControlInterceptor 添加到其中

2.3. 使 MyBatis 拦截器靠前执行的注册方式

为了使某个 MyBatis 拦截器执行顺序靠前,需要使其注册顺序靠后,可以处理 Spring ContextRefreshedEvent 事件时进行注册,简单示例如下:

@Service
public class TestMyBatisInterceptorRegister1 {

    @EventListener
    public void handleContextRefreshedEvent(ContextRefreshedEvent contextRefreshedEvent) {
        TestMyBatisInterceptor testMyBatisInterceptor = new TestMyBatisInterceptor();
        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        Map<String, SqlSessionFactory> sqlSessionFactoryMap = applicationContext.getBeansOfType(SqlSessionFactory.class);
        for (Map.Entry<String, SqlSessionFactory> entry : sqlSessionFactoryMap.entrySet()) {
            SqlSessionFactory sqlSessionFactory = entry.getValue();
            sqlSessionFactory.getConfiguration().addInterceptor(testMyBatisInterceptor);
        }
    }
}

TestMyBatisInterceptor 类是 MyBatis 拦截器

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以通过自定义拦截器来实现在特定的Mapper上进行动态拦截。首先,创建一个实现了`Interceptor`接口的自定义拦截器类,然后在该类中实现需要的拦截逻辑。 下面是一个示例代码: ```java @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); String mapperId = mappedStatement.getId(); if (mapperId.contains(".yourMapperMethod")) { // 执行拦截逻辑 // ... } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 设置属性 } } ``` 在上述示例中,拦截器通过判断`mapperId`是否包含特定的Mapper方法名来确定是否执行拦截逻辑。您可以根据实际需求自定义拦截器的逻辑。 接下来,需要将自定义拦截器注册MyBatis的配置中。在Spring Boot中,可以通过配置类来实现: ```java @Configuration public class MyBatisConfig { @Autowired private MyInterceptor myInterceptor; @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); // 设置拦截器 Interceptor[] interceptors = new Interceptor[]{myInterceptor}; sessionFactory.setPlugins(interceptors); return sessionFactory.getObject(); } } ``` 在上述代码中,将自定义拦截器通过`setPlugins()`方法设置到`SqlSessionFactory`中。 这样,自定义拦截器就会在特定的Mapper方法被调用时进行拦截。您可以根据需要在拦截器中实现相应的逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值