Mybatis攔截器介紹及其應用
1、介紹
Mybatis攔截器設計的初衷就是為了供用戶在某些時候可以實現自己的邏輯而不必去動Mybatis固有的邏輯。通過Mybatis攔截器我們可以攔截某些方法的調用,我們可以選擇在這些被攔截的方法執行前后加上某些邏輯,也可以在執行這些被攔截的方法時執行自己的邏輯而不再執行被攔截的方法。所以Mybatis攔截器的使用范圍是非常廣泛的。
2、Mybatis部分核心對象
Mybatis核心對象 | 解釋 |
---|---|
SqlSession | 作為MyBatis工作的主要頂層API,表示和數據庫交互的會話,完成必要數據庫增刪改查功能 |
Executor | MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護 |
StatementHandler | 封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合 |
ParameterHandler | 負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數 |
ResultSetHandler | 負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合 |
TypeHandler | 負責java數據類型和jdbc數據類型之間的映射和轉換 |
MappedStatement | MappedStatement維護了一條mapper.xml文件里面 select 、update、delete、insert節點的封裝 |
SqlSource | 負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,并返回 |
BoundSql | 表示動態生成的SQL語句以及相應的參數信息 |
Configuration | MyBatis所有的配置信息都維持在Configuration對象之中 |
3、攔截器種類
3.1、ParameterHandler 參數攔截器:
ParameterHandler攔截器類型用于攔截MyBatis的參數處理過程。它在參數設置到PreparedStatement對象之前攔截并修改參數。你可以通過實現ParameterHandler接口來自定義參數處理邏輯,例如對參數進行加密、解密、校驗或轉換等操作。
3.2、ResultSetHandler 結果集攔截器:
ResultSetHandler攔截器類型用于攔截MyBatis的結果集處理過程。它在從JDBC結果集中獲取數據并映射到Java對象或集合之前攔截并修改結果。你可以通過實現ResultSetHandler接口來自定義結果集處理邏輯,例如對結果進行加工、過濾、緩存或轉換等操作。
3.3、StatementHandler 語句攔截器:
StatementHandler攔截器類型用于攔截MyBatis的SQL語句處理過程。它在SQL語句執行之前攔截并修改SQL語句、設置參數或進行其他操作。你可以通過實現StatementHandler接口來自定義SQL語句處理邏輯,例如動態修改SQL語句、添加分頁邏輯、實現緩存等。
3.4、Executor執行攔截器:
攔截執行器的方法,主要負責SQL的執行,包括INSERT、UPDATE、DELETE等操作以及SELECT查詢操作。通過攔截Executor接口的方法,可以實現對數據庫操作前后的統一處理,比如開啟事務、記錄日志、分頁處理、二級緩存控制等。
4、實際應用
4.1、自定義攔截器
package com.springboot.cli;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Method;
import java.util.Properties;/*** 類說明:@Intercepts注解用于標注一個類是一個MyBatis攔截器;@Signature注解用于指定一個方法的簽名,其中包括方法所在的接口、方法名以及方法的參數列表* type參數指定了被攔截的接口類型,比如Executor.class表示要攔截Executor接口的方法。* method參數指定了要攔截的方法名。* args參數是一個Class數組,用來指定方法的參數類型列表,通過這些參數類型可以唯一確定要攔截的方法。* 如果需要攔截多個不同類型的方法,可以在@Intercepts注解中指定多個@Signature注解。** @author liangtl* @Date 2024/7/14*/
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MybatisInterceptor implements Interceptor {private Properties properties;@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 代理對象Object target = invocation.getTarget();// 代理方法Method method = invocation.getMethod();// 方法參數Object[] args = invocation.getArgs();// 如果args長度大于1,說明有附帶的其他參數Object parameter = null;if (args.length > 1) {// 查詢參數parameter = args[1];}MappedStatement mappedStatement = (MappedStatement) args[0];// sqlIdString sqlId = mappedStatement.getId();// 拼接完成的sqlString sql = mappedStatement.getBoundSql(parameter).getSql();// 這里一定需要使用invocation.proceed()返回,以保證sql繼續執行。// 如果想進行判斷然后中斷sql執行,可以返回emptyList。return invocation.proceed();}/*** 生成MyBatis攔截器代理對象(非必須)*/@Overridepublic Object plugin(Object target) {if(target instanceof StatementHandler){//調用插件return Plugin.wrap(target, this);}return target;}/*** 設置插件屬性(直接通過Spring的方式獲取屬性,所以這個方法一般也用不到)* 項目啟動的時候數據就會被加載*/@Overridepublic void setProperties(Properties properties) {//賦值成員變量,在其他方法使用。this.properties = properties;}
}
4.2、注冊攔截器
攔截器注冊有兩種方式,一種是在配置文件中配置,第二種是通過代碼的形式。
這里演示使用代碼進行攔截器注冊。
package com.springboot.cli;import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;/*** 數據源配置Demo** @author liangtl* @Date 2024/7/14*/@Configuration
public class DataSourceConfig {@Value("${test.datasource.url}")private String url;@Value("${test.datasource.username}")private String user;@Value("${test.datasource.password}")private String password;@Value("${test.datasource.driverClassName}")private String driverClass;@Bean(name = "dataSource")public DataSource dataSource() {PooledDataSource dataSource = new PooledDataSource();dataSource.setDriver(driverClass);dataSource.setUrl(url);dataSource.setUsername(user);dataSource.setPassword(password);return dataSource;}@Bean(name = "transactionManager")public DataSourceTransactionManager dataSourceTransactionManager() {return new DataSourceTransactionManager(dataSource());}@Bean(name = "sqlSessionFactory")public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);sessionFactory.setPlugins(new MybatisInterceptor());// 設置mapper文件位置sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:**/**/mapper/*.xml"));// 設置實體類映射規則: 下劃線 -> 駝峰org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();configuration.setMapUnderscoreToCamelCase(true);sessionFactory.setConfiguration(configuration);return sessionFactory.getObject();}
}
在我們定義的sqlSessionFactory這個bean中,sessionFactory.setPlugins(new MybatisInterceptor());
這一行代碼就是我們注冊自定義攔截器的地方。
至此,Mybatis攔截器就已經從自定義到注冊完成,當我們注入的是sqlSessionFactory
這個Factory時,攔截器就會生效。
5、擴展
有些時候我們可能需要在攔截器中注入一些我們需要的類來完成一些特定的邏輯,于是我們默認使用@Autowired
去將其注入,結果發生代碼執行時報了NPE,因為注入的屬性為null。
原因如下:
因為攔截器對象是由Servlet容器負責創建和管理的,而不是由Spring容器管理的。因此,Spring容器無法感知并注入攔截器中的屬性。
現在已經知道原因了,那么就可以解決這個問題。
方法一:將攔截器作為一個Spring Bean進行配置,交由Spring容器進行管理
方法二:既然攔截器中無法注入,那就不注入了。新增一個有參構造使用構造器的方式注入屬性
方法三:可以通過實現ApplicationContextAware接口或者直接在攔截器中獲取ApplicationContext對象,然后從ApplicationContext中獲取需要的Bean
參考博客:
Mybatis攔截器_@intercepts-CSDN博客
MyBatis攔截器四種類型和自定義攔截器的使用流程_mybatis 攔截器-CSDN博客