最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在springboot中如何給mybatis加攔截器

 更新時(shí)間:2022年08月26日 10:53:52   作者:頭未禿  
這篇文章主要介紹了在springboot中如何給mybatis加攔截器,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

攔截器的作用就是我們可以攔截某些方法的調(diào)用,在目標(biāo)方法前后加上我們自己邏輯

Mybatis攔截器設(shè)計(jì)的一個(gè)初衷是為了供用戶在某些時(shí)候可以實(shí)現(xiàn)自己的邏輯而不必去動(dòng)Mybatis固有的邏輯。

mybatis 自定義攔截器

1、實(shí)現(xiàn)Interceptor 接口,并添加攔截注解 @Intercepts

2、配置文件中添加攔截器

1、實(shí)現(xiàn)Interceptor接口,并添加攔截注解 @Intercepts

mybatis 攔截器默認(rèn)可攔截的類型只有四種

  • Executor:攔截執(zhí)行器的方法。
  • ParameterHandler:攔截參數(shù)的處理。
  • ResultHandler:攔截結(jié)果集的處理。
  • StatementHandler:攔截Sql語法構(gòu)建的處理。

對(duì)于我們的自定義攔截器必須使用 mybatis 提供的注解來指明我們要攔截的是四類中的哪一個(gè)類接口

具體規(guī)則如下:

a:Intercepts 攔截器:標(biāo)識(shí)我的類是一個(gè)攔截器

b:Signature 署名:則是指明我們的攔截器需要攔截哪一個(gè)接口的哪一個(gè)方法

  • type 對(duì)應(yīng)四類接口中的某一個(gè),比如是 Executor
  • method 對(duì)應(yīng)接口中的哪類方法,比如 Executor 的 update 方法
  • args 對(duì)應(yīng)接口中的哪一個(gè)方法,比如 Executor 中 query 因?yàn)橹剌d原因,方法有多個(gè),args 就是指明參數(shù)類型,從而確定是哪一個(gè)方法
@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 {
    /**
     * 這個(gè)方法很好理解
     * 作用只有一個(gè):我們不是攔截方法嗎,攔截之后我們要做什么事情呢?
     *      這個(gè)方法里面就是我們要做的事情
     *
     * 解釋這個(gè)方法前,我們一定要理解方法參數(shù) {@link Invocation} 是個(gè)什么鬼?
     * 1 我們知道,mybatis攔截器默認(rèn)只能攔截四種類型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler
     * 2 不管是哪種代理,代理的目標(biāo)對(duì)象就是我們要攔截對(duì)象,舉例說明:
     *      比如我們要攔截 {@link Executor#update(MappedStatement ms, Object parameter)} 方法,
     *      那么 Invocation 就是這個(gè)對(duì)象,Invocation 里面有三個(gè)參數(shù) target method args
     *          target 就是 Executor
     *          method 就是 update
     *          args   就是 MappedStatement ms, Object parameter
     *
     *   如果還是不能理解,我再舉一個(gè)需求案例:看下面方法代碼里面的需求
     *
     *  該方法在運(yùn)行時(shí)調(diào)用
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        /*
         * 需求:我們需要對(duì)所有更新操作前打印查詢語句的 sql 日志
         * 那我就可以讓我們的自定義攔截器 MyInterceptor 攔截 Executor 的 update 方法,在 update 執(zhí)行前打印sql日志
         * 比如我們攔截點(diǎn)是 Executor 的 update 方法 :  int update(MappedStatement ms, Object parameter)
         *
         * 那當(dāng)我們?nèi)罩敬蛴〕晒χ螅覀兪遣皇沁€需要調(diào)用這個(gè)query方法呢,如何如調(diào)用呢?
         * 所以就出現(xiàn)了 Invocation 對(duì)象,它這個(gè)時(shí)候其實(shí)就是一個(gè) Executor,而且 method 對(duì)應(yīng)的就是 query 方法,我們
         * 想要調(diào)用這個(gè)方法,只需要執(zhí)行 invocation.proceed()
         */
        /* 因?yàn)槲覕r截的就是Executor,所以我可以強(qiáng)轉(zhuǎn)為 Executor,默認(rèn)情況下,這個(gè)Executor 是個(gè) SimpleExecutor */
        Executor executor = (Executor)invocation.getTarget();
        /*
         * Executor 的 update 方法里面有一個(gè)參數(shù) MappedStatement,它是包含了 sql 語句的,所以我獲取這個(gè)對(duì)象
         * 以下是偽代碼,思路:
         * 1 通過反射從 Executor 對(duì)象中獲取 MappedStatement 對(duì)象
         * 2 從 MappedStatement 對(duì)象中獲取 SqlSource 對(duì)象
         * 3 然后從 SqlSource 對(duì)象中獲取獲取 BoundSql 對(duì)象
         * 4 最后通過 BoundSql#getSql 方法獲取 sql
         */
        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);
        /*
         * 現(xiàn)在日志已經(jīng)打印,需要調(diào)用目標(biāo)對(duì)象的方法完成 update 操作
         * 我們直接調(diào)用 invocation.proceed() 方法
         * 進(jìn)入源碼其實(shí)就是一個(gè)常見的反射調(diào)用 method.invoke(target, args)
         * target 對(duì)應(yīng) Executor對(duì)象
         * method 對(duì)應(yīng) Executor的update方法
         * args   對(duì)應(yīng) Executor的update方法的參數(shù)
         */
        return invocation.proceed();
    }
    /**
     * 這個(gè)方法也很好理解
     * 作用就只有一個(gè):那就是Mybatis在創(chuàng)建攔截器代理時(shí)候會(huì)判斷一次,當(dāng)前這個(gè)類 MyInterceptor 到底需不需要生成一個(gè)代理進(jìn)行攔截,
     * 如果需要攔截,就生成一個(gè)代理對(duì)象,這個(gè)代理就是一個(gè) {@link Plugin},它實(shí)現(xiàn)了jdk的動(dòng)態(tài)代理接口 {@link InvocationHandler},
     * 如果不需要代理,則直接返回目標(biāo)對(duì)象本身
     *
     * Mybatis為什么會(huì)判斷一次是否需要代理呢?
     * 默認(rèn)情況下,Mybatis只能攔截四種類型的接口:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler
     * 通過 {@link Intercepts} 和 {@link Signature} 兩個(gè)注解共同完成
     * 試想一下,如果我們開發(fā)人員在自定義攔截器上沒有指明類型,或者隨便寫一個(gè)攔截點(diǎn),比如Object,那Mybatis瘋了,難道所有對(duì)象都去攔截
     * 所以Mybatis會(huì)做一次判斷,攔截點(diǎn)看看是不是這四個(gè)接口里面的方法,不是則不攔截,直接返回目標(biāo)對(duì)象,如果是則需要生成一個(gè)代理
     *
     *  該方法在 mybatis 加載核心配置文件時(shí)被調(diào)用
     */
    @Override
    public Object plugin(Object target) {
        /*
         * 看了這個(gè)方法注釋,就應(yīng)該理解,這里的邏輯只有一個(gè),就是讓mybatis判斷,要不要進(jìn)行攔截,然后做出決定是否生成一個(gè)代理
         *
         * 下面代碼什么鬼,就這一句就搞定了?
         * Mybatis判斷依據(jù)是利用反射,獲取這個(gè)攔截器 MyInterceptor 的注解 Intercepts和Signature,然后解析里面的值,
         * 1 先是判斷要攔截的對(duì)象是四個(gè)類型中 Executor、StatementHandler、ParameterHandler、 ResultSetHandler 的哪一個(gè)
         * 2 然后根據(jù)方法名稱和參數(shù)(因?yàn)橛兄剌d)判斷對(duì)哪一個(gè)方法進(jìn)行攔截  Note:mybatis可以攔截這四個(gè)接口里面的任一一個(gè)方法
         * 3 做出決定,是返回一個(gè)對(duì)象呢還是返回目標(biāo)對(duì)象本身(目標(biāo)對(duì)象本身就是四個(gè)接口的實(shí)現(xiàn)類,我們攔截的就是這四個(gè)類型)
         *
         * 好了,理解邏輯我們寫代碼吧~~~  What !!! 要使用反射,然后解析注解,然后根據(jù)參數(shù)類型,最后還要生成一個(gè)代理對(duì)象
         * 我一個(gè)小白我怎么會(huì)這么高大上的代碼嘛,怎么辦?
         *
         * 那就是使用下面這句代碼吧  哈哈
         * mybatis 早就考慮了這里的復(fù)雜度,所以提供這個(gè)靜態(tài)方法來實(shí)現(xiàn)上面的邏輯
         */
        return Plugin.wrap(target, this);
    }
    /**
     * 這個(gè)方法最好理解,如果我們攔截器需要用到一些變量參數(shù),而且這個(gè)參數(shù)是支持可配置的,
     *  類似Spring中的@Value("${}")從application.properties文件獲取
     * 這個(gè)時(shí)候我們就可以使用這個(gè)方法
     *
     * 如何使用?
     * 只需要在 mybatis 配置文件中加入類似如下配置,然后 {@link Interceptor#setProperties(Properties)} 就可以獲取參數(shù)
     *      <plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor">
     *           <property name="username" value="LiuYork"/>
     *           <property name="password" value="123456"/>
     *      </plugin>
     *      方法中獲取參數(shù):properties.getProperty("username");
     *
     * 問題:為什么要存在這個(gè)方法呢,比如直接使用 @Value("${}") 獲取不就得了?
     * 原因是 mybatis 框架本身就是一個(gè)可以獨(dú)立使用的框架,沒有像 Spring 這種做了很多依賴注入的功能
     *
     *  該方法在 mybatis 加載核心配置文件時(shí)被調(diào)用 
     */
    @Override
    public void setProperties(Properties properties) {
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        // TODO: 2019/2/28  業(yè)務(wù)邏輯處理...
    }
}

三個(gè)核心方法都加了詳細(xì)的注釋,而且結(jié)合案例需求說明問題

那么多文字不想行看,沒關(guān)系有概括

總結(jié):

1.在mybatis中可被攔截的類型有四種(按照攔截順序)

  • Executor:攔截執(zhí)行器的方法。
  • ParameterHandler:攔截參數(shù)的處理。
  • ResultHandler:攔截結(jié)果集的處理。
  • StatementHandler:攔截Sql語法構(gòu)建的處理。

2.各個(gè)參數(shù)的含義

  • @Intercepts:標(biāo)識(shí)該類是一個(gè)攔截器;
  • @Signature:指明自定義攔截器需要攔截哪一個(gè)類型,哪一個(gè)方法;

2.1 type:對(duì)應(yīng)四種類型中的一種;

2.2 method:對(duì)應(yīng)接口中的哪個(gè)方法;

2.3 args:對(duì)應(yīng)哪一個(gè)方法參數(shù)類型(因?yàn)榭赡艽嬖谥剌d方法);

接下來我們看看 Plugin 類

package org.apache.ibatis.plugin;
/**
 * Plugin 類其實(shí)就是一個(gè)代理類,因?yàn)樗鼘?shí)現(xiàn)了jdk動(dòng)態(tài)代理接口 InvocationHandler
 * 我們核心只需要關(guān)注兩個(gè)方法
 * wrap:
 *      如果看懂了代碼案例1的例子,那么這個(gè)方法很理解,這個(gè)方法就是 mybatis 提供給開發(fā)人員使用的一個(gè)工具類方法,
 *      目的就是幫助開發(fā)人員省略掉 反射解析注解 Intercepts 和 Signature,有興趣的可以去看看源碼 Plugin#getSignatureMap 方法
 *
 * invoke:
 *      這個(gè)方法就是根據(jù) wrap 方法的解析結(jié)果,判斷當(dāng)前攔截器是否需要進(jìn)行攔截,
 *      如果需要攔截:將 目標(biāo)對(duì)象+目標(biāo)方法+目標(biāo)參數(shù) 封裝成一個(gè) Invocation 對(duì)象,給我們自定義的攔截器 MyInterceptor 的 intercept 方法
 *                   這個(gè)時(shí)候就剛好對(duì)應(yīng)上了上面案例1中對(duì) intercept 方法的解釋了,它就是我們要處理自己邏輯的方法,
 *                   處理好了之后是否需要調(diào)用目標(biāo)對(duì)象的方法,比如上面說的 打印了sql語句,是否還要查詢數(shù)據(jù)庫(kù)呢?答案是肯定的
 *      如果不需要攔截:則直接調(diào)用目標(biāo)對(duì)象的方法
 *                   比如直接調(diào)用 Executor 的 update 方法進(jìn)行更新數(shù)據(jù)庫(kù)
 *
 */
class Plugin implements InvocationHandler {
    public static Object wrap(Object target, Interceptor interceptor) {
        // 省略
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 省略
    }
}

貼一段網(wǎng)上的通用解釋吧:

  • Plugin的wrap方法,它根據(jù)當(dāng)前的Interceptor上面的注解定義哪些接口需要攔截,然后判斷當(dāng)前目標(biāo)對(duì)象是否有實(shí)現(xiàn)對(duì)應(yīng)需要攔截的接口,如果沒有則返回目標(biāo)對(duì)象本身,如果有則返回一個(gè)代理對(duì)象。
  • 而這個(gè)代理對(duì)象的InvocationHandler正是一個(gè)Plugin。所以當(dāng)目標(biāo)對(duì)象在執(zhí)行接口方法時(shí),如果是通過代理對(duì)象執(zhí)行的,則會(huì)調(diào)用對(duì)應(yīng)InvocationHandler的invoke方法,也就是Plugin的invoke方法。
  • 所以接著我們來看一下該invoke方法的內(nèi)容。
  • 這里invoke方法的邏輯是:如果當(dāng)前執(zhí)行的方法是定義好的需要攔截的方法,則把目標(biāo)對(duì)象、要執(zhí)行的方法以及方法參數(shù)封裝成一個(gè)Invocation對(duì)象,再把封裝好的Invocation作為參數(shù)傳遞給當(dāng)前攔截器的intercept方法。
  • 如果不需要攔截,則直接調(diào)用當(dāng)前的方法。
  • Invocation中定義了定義了一個(gè)proceed方法,其邏輯就是調(diào)用當(dāng)前方法,所以如果在intercept中需要繼續(xù)調(diào)用當(dāng)前方法的話可以調(diào)用invocation的procced方法。

這就是Mybatis中實(shí)現(xiàn)Interceptor攔截的一個(gè)思想

2、在配置文件中添加攔截器

在springboot中要給mybatis加上這個(gè)攔截器,有三種方法,前兩種方法在啟動(dòng)項(xiàng)目時(shí)不會(huì)自動(dòng)調(diào)用自定義攔截器的setProperties方法。

攔截器順序

1、不同攔截器順序

Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler

2、對(duì)于同一個(gè)類型的攔截器的不同對(duì)象攔截順序:

在 mybatis 核心配置文件根據(jù)配置的位置,攔截順序是 從上往下

(1)第一種

直接給自定義攔截器添加一個(gè) @Component注解,當(dāng)調(diào)用sql時(shí)結(jié)果如下,可以看到攔截器生效了,但是啟動(dòng)時(shí)候并沒有自動(dòng)調(diào)用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 {
		//業(yè)務(wù)代碼
	}
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
	}
}

(2)第二種

在配置類里添加攔截器,這種方法結(jié)果同上,也不會(huì)自動(dòng)調(diào)用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屬性不能同時(shí)指定

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>

可以看到,在啟動(dòng)項(xiàng)目的時(shí)候setProperties被自動(dòng)調(diào)用了

總結(jié):前兩種方法可以在初始化自定義攔截器的時(shí)候通過 @Value 注解直接初始化需要的參數(shù)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • 詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法

    這篇文章主要介紹了詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java圖形用戶界面設(shè)計(jì)(Swing)的介紹

    Java圖形用戶界面設(shè)計(jì)(Swing)的介紹

    看到多數(shù)人提到 Java 就以為是網(wǎng)絡(luò)開發(fā),其實(shí)不是這樣的,Java 也可以開發(fā)應(yīng)用程序,而且可以開發(fā)出漂亮的圖形用戶界面的應(yīng)用程序,因此,我寫下這篇文章,希望能帶你進(jìn)入 Java 圖形用戶界面設(shè)計(jì)之門。
    2016-07-07
  • Java如何通過反射獲取私有構(gòu)造、私有對(duì)象、私有字段、私有方法

    Java如何通過反射獲取私有構(gòu)造、私有對(duì)象、私有字段、私有方法

    這篇文章主要介紹了Java如何通過反射獲取私有構(gòu)造、私有對(duì)象、私有字段、私有方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java中的升序和降序問題

    Java中的升序和降序問題

    這篇文章主要介紹了Java中的升序和降序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Spring中的后置處理器BeanPostProcessor詳解

    Spring中的后置處理器BeanPostProcessor詳解

    這篇文章主要介紹了Spring中的后置處理器BeanPostProcessor詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 關(guān)于MVC設(shè)計(jì)模式及流程解析

    關(guān)于MVC設(shè)計(jì)模式及流程解析

    這篇文章主要介紹了關(guān)于MVC設(shè)計(jì)模式及流程解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • springboot如何使用AOP做訪問請(qǐng)求日志

    springboot如何使用AOP做訪問請(qǐng)求日志

    這篇文章主要介紹了springboot如何使用AOP做訪問請(qǐng)求日志,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • RabbitMQ高級(jí)應(yīng)用之消費(fèi)端限流策略basicQos詳解

    RabbitMQ高級(jí)應(yīng)用之消費(fèi)端限流策略basicQos詳解

    這篇文章主要介紹了RabbitMQ高級(jí)應(yīng)用之消費(fèi)端限流策略basicQos詳解,高并發(fā)情況下,隊(duì)列里面一瞬間就就積累了上萬條數(shù)據(jù),但是消費(fèi)者無法同時(shí)處理這么多請(qǐng)求,這種場(chǎng)景下我們就需要對(duì)消費(fèi)端進(jìn)行限流,需要的朋友可以參考下
    2023-08-08
  • mybatis動(dòng)態(tài)生成sql語句的實(shí)現(xiàn)示例

    mybatis動(dòng)態(tài)生成sql語句的實(shí)現(xiàn)示例

    在MyBatis中,動(dòng)態(tài)SQL是一個(gè)非常重要的特性,它允許我們根據(jù)條件動(dòng)態(tài)地生成SQL語句,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • 淺談SpringBoot中properties、yml、yaml的優(yōu)先級(jí)

    淺談SpringBoot中properties、yml、yaml的優(yōu)先級(jí)

    優(yōu)先級(jí)低的配置會(huì)被先加載,所以優(yōu)先級(jí)高的配置會(huì)覆蓋優(yōu)先級(jí)低的配置,本文就來介紹一下SpringBoot中properties、yml、yaml的優(yōu)先級(jí),感興趣的可以了解一下
    2023-08-08

最新評(píng)論

改则县| 兴和县| 惠州市| 洞口县| 高邮市| 呼伦贝尔市| 达尔| 开远市| 饶平县| 肥城市| 乐清市| 桃园市| 怀宁县| 同德县| 楚雄市| 丰原市| 临西县| 仁寿县| 仙居县| 嘉黎县| 亳州市| 万载县| 庄浪县| 尚义县| 营口市| 抚松县| 靖宇县| 如东县| 永顺县| 镇安县| 南木林县| 宁强县| 永和县| 利津县| 大田县| 秭归县| 布拖县| 凤冈县| 新郑市| 古蔺县| 成武县|