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

Mybatis?plugin的使用及原理示例解析

 更新時(shí)間:2023年09月26日 08:51:29   作者:戰(zhàn)斧  
這篇文章主要為大家介紹了?Mybatis?plugin的使用及原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

上次,我們說過了mybatis+springboot時(shí)的啟動(dòng)與執(zhí)行流程,也介紹過mybatis的執(zhí)行器和緩存,今天,我們來(lái)看看mybatis 的另一個(gè)大功能 —— plugin

一、Mybatis Plugin 是什么

MyBatis的plugin插件是用來(lái)攔截SQL執(zhí)行的,對(duì)SQL進(jìn)行增強(qiáng)的一種機(jī)制。

MyBatis的Plugin實(shí)現(xiàn)基于JDK動(dòng)態(tài)代理機(jī)制,在MyBatis初始化過程中,可以為指定的攔截對(duì)象生成代理對(duì)象,當(dāng)攔截對(duì)象執(zhí)行某個(gè)方法時(shí),代理會(huì)先執(zhí)行插件中的邏輯,再執(zhí)行原有邏輯。插件可以在原有邏輯前后添加自己的邏輯或者完全替換原有邏輯

如果你使用過spring的話,會(huì)自然的想到spring的AOP特性,兩者都是利用代理來(lái)實(shí)現(xiàn)功能的增強(qiáng)

二、Mybatis Plugin 的實(shí)例

這是一個(gè)舊項(xiàng)目,在后期對(duì)接Oracle后,有很多sql報(bào)了錯(cuò),其原因是使用 instr() 函數(shù)時(shí),由于參數(shù)是外部傳入的,有時(shí)候可能會(huì)傳來(lái)一個(gè)幾千長(zhǎng)度的字符串,從而導(dǎo)致instr 超長(zhǎng)報(bào)錯(cuò)。因?yàn)檫@樣的sql還有很多,不可能一一去改,所以必須使用功能增強(qiáng)的方式來(lái)解決

在直接上示例之前,我們先看看官方提供的接口 Interceptor.java ,只要實(shí)現(xiàn)了該接口,就可以在指定位置發(fā)揮作用

package org.apache.ibatis.plugin;
public interface Interceptor {
  /**
  * intercept方法就是要進(jìn)行攔截的時(shí)候要執(zhí)行的方法
  */
  Object intercept(Invocation invocation) throws Throwable;
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  default void setProperties(Properties properties) {
    // NOP
  }
}

當(dāng)然,這個(gè)接口還需要配合另一個(gè)注解 @Intercepts 使用,我們結(jié)合案例寫一個(gè)插件看看

@Component
@Intercepts({ @Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = { Connection.class, Integer.class }) })
public class ExamplePlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        String newSql = null;
        // 改造sql部分省略,主要是將 instr() 拆分成 instr() or instr() 的形式以降低每個(gè)括號(hào)內(nèi)	的長(zhǎng)度...
        // newsql = sql.replace(...)
        // 把新的sql通過反射重新設(shè)置回去
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql , newSql);
        Object returnVal = invocation.proceed();
        return returnVal;
    }
}

不難看出,配上注解后,該插件的意思就是針對(duì) StatementHandler.prepare(Connection, Integer) 方法進(jìn)行增強(qiáng),我們實(shí)際運(yùn)行下看看:

如圖,最終走到了我們寫的插件的 intercept 方法中

需要注意的是 @Intercepts 注解內(nèi)支持配置 @Signature 數(shù)組,并以逗號(hào)分割。也就是說一個(gè)攔截器其實(shí)可以攔截多個(gè)類的方法,如下

@Intercepts({
        @Signature(
                type = ResultSetHandler.class,
                method = "handleResultSets", 
                args = {Statement.class}),
        @Signature(type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})

三、Mybatis Plugin 原理

1. Mybatis 支持哪些 Plugin

其實(shí)從上面,案例看出,我們對(duì)插件的設(shè)置主要是通過 @Intercepts 內(nèi)的 @Signature 注解實(shí)現(xiàn)的

@Intercepts({ @Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = { Connection.class, Integer.class }) })

其中,type 就是作用的接口,method 和 args 則能確定唯一方法(單用方法名,可能有方法重載的情況)
但是,是不是這些屬性可以隨便填呢?其實(shí)不是的,mybatis沒有做的那么自由,其更像Spring中的postProcessor機(jī)制,只在固定的幾個(gè)位置有預(yù)留點(diǎn),讓你可以自定義增強(qiáng),而不是開放所有位置。

這里的Pulgin只針對(duì)以下四個(gè)接口有增強(qiáng)預(yù)留點(diǎn),它們分別是

  • Statementhandler
    用于處理JDBC Statement對(duì)象的相關(guān)操作,將SQL語(yǔ)句中的占位符進(jìn)行替換,然后使用Statement對(duì)象執(zhí)行SQL語(yǔ)句
  • Resultsethandler
    主要負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集轉(zhuǎn)化為Java對(duì)象,然后返還給調(diào)用方
  • ParameterHandler
    主要用于處理Java對(duì)象與JDBC參數(shù)的映射,并將其轉(zhuǎn)化為JDBC參數(shù)。
  • Executor
    更頂層的設(shè)計(jì),能對(duì)上三種類進(jìn)行調(diào)用,執(zhí)行SQL語(yǔ)句,并獲取執(zhí)行結(jié)果

其具體調(diào)用鏈路如下:

2. myBatis 如何加載 Plugin

即我們自己創(chuàng)建了個(gè) Interceptor 實(shí)現(xiàn)類,也使用了 @Intercepts 注解,但這個(gè)類是如何被mybatis加載的呢?

2.1 springboot 項(xiàng)目

我們?nèi)砸陨掀恼碌膕pringboot+mybatis為例,那么此處便又要提到spring-boot的自動(dòng)配置了,我們看下 MybatisAutoConfiguration (mybatis-spring-boot-autoconfigure2.1.4版本)這個(gè)自動(dòng)配置類,看其構(gòu)造方法

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
  // 省略部分代碼
  public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
      ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
      ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    this.properties = properties;
    this.interceptors = interceptorsProvider.getIfAvailable();
    this.typeHandlers = typeHandlersProvider.getIfAvailable();
    this.languageDrivers = languageDriversProvider.getIfAvailable();
    this.resourceLoader = resourceLoader;
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
  }
}

其構(gòu)造方法的第二個(gè)參數(shù) :ObjectProvider<Interceptor[]>

ObjectProvider 是在spring 4.3 引入的一種注入方式,它可以檢索指定的類型。

然后通過 getIfAvailable 和 getIfUnique 從spring容器中檢索出對(duì)應(yīng)對(duì)象

因?yàn)槲覀円呀?jīng)在自定義的 ExamplePlugin 上使用了@Component 的注解,所以此處使用自動(dòng)注入,能獲取到我們的插件理所當(dāng)然。

而后再把該值賦給 sqlSessionFactoryBean, 然后再賦給 mybatis 真正的配置類 Configuration。至此,我們的插件就被 mybatis 系統(tǒng)所成功加載了。

2.2 spring 項(xiàng)目

如果還沒有使用上spring-boot,沒有所謂的自動(dòng)配置,那也無(wú)妨,只是需要手動(dòng)額外配置一點(diǎn)參數(shù)也是同樣的。

如:已經(jīng)在 application.properties 配置了mybatis 配置文件

mybatis.config.location: classpath:/mybatis-config.xml

然后在mybatis-config.xml 里加上如下配置

<configuration>    
	<plugins>
        <plugin interceptor="com.zhanfu.spring.demo.utils.ExamplePlugin"/>
    </plugins>
</configuration>

這樣也能達(dá)到,將指定插件放入 mybatis 框架的效果

3. Plugin 生效原理

上面我們講了,如何寫一個(gè)插件,以及插件是怎么交給 myBatis框架的,現(xiàn)在要談最重要的內(nèi)容了。即myBatis 是如何利用插件的。

上文我們已經(jīng)了解到了,所有的插件實(shí)例都被放入了 myBatis 的總配置類 Configuration 去管理,成為了該類的一個(gè)屬性interceptorChain ,該類詳情如下:

public class InterceptorChain {
  // 所有的插件都存在這個(gè) List 中
  private final List<Interceptor> interceptors = new ArrayList<>();
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}

所以我們只要看該類拿這些插件做了什么即可,可以看到,該類對(duì)所有新建的目標(biāo)對(duì)象,都進(jìn)行了 pluginAll 操作,結(jié)合上圖,我們不難看出,該方法其實(shí)就是遍歷所有插件,然后調(diào)用每個(gè)插件的 plugin() 方法 生成一個(gè)新對(duì)象,然后下一個(gè)插件拿這個(gè)新對(duì)象再 plugin() 生成一個(gè)新對(duì)象,實(shí)際上構(gòu)成了一套鏈?zhǔn)降那短?/p>

那么plugin() 方法到底做了什么呢?我們來(lái)看看回頭再來(lái)看看 Interceptor 接口里,該方法的默認(rèn)實(shí)現(xiàn)

  // Interceptor.java
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  // Plugin.java
  public static Object wrap(Object target, Interceptor interceptor) {
    // 從插件的注解中,解析出該插件可作用的接口,以及該類下的哪些方法
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    // 找到插件可作用的接口和目標(biāo)類的中所有重合的接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    // 如果有重合的接口,則生成jdk代理并返回。注意,interceptor是我們的插件對(duì)象,signatureMap是插件注釋解析到的類與方法
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    // 如果沒有,則返回原對(duì)象
    return target;
  }

綜上,不難看出,只有生成指定的四種實(shí)例時(shí),才會(huì)進(jìn)入上述代碼生成代理,最后返還的其實(shí)就是代理對(duì)象。需要注意的是,此時(shí)的代理是能夠代理這些接口的所有方法的,要想實(shí)現(xiàn)指定方法才使用代理,還得依靠代理的 invoke 方法內(nèi)去篩選

  // Plugin.java
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 只有注解上指定方法才能走插件對(duì)象的 intercept 方法
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      // 其他方法盡管經(jīng)過代理,但其實(shí)什么也沒做,直接調(diào)用原對(duì)象去了
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

以上就是 Mybatis plugin 的使用及原理解析的詳細(xì)內(nèi)容,更多關(guān)于 Mybatis plugin使用原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring工程中集成多個(gè)redis數(shù)據(jù)源的思路詳解

    Spring工程中集成多個(gè)redis數(shù)據(jù)源的思路詳解

    Spring項(xiàng)目集成雙Redis數(shù)據(jù)源需配置兩個(gè)連接工廠與RedisTemplate,通過@Primary指定默認(rèn)Bean,@Qualifier注入特定Bean,支持集群環(huán)境,本文給大家介紹Spring工程中集成多個(gè)redis數(shù)據(jù)源的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2025-07-07
  • 在Java中Collection的一些常用方法總結(jié)

    在Java中Collection的一些常用方法總結(jié)

    今天給大家?guī)?lái)的知識(shí)是關(guān)于Java的,文章圍繞著Java中Collection的一些常用方法展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java設(shè)計(jì)模式之java迭代器模式詳解

    Java設(shè)計(jì)模式之java迭代器模式詳解

    這篇文章主要介紹了java迭代器模式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • Java實(shí)現(xiàn)根據(jù)模板高效生成Word文檔

    Java實(shí)現(xiàn)根據(jù)模板高效生成Word文檔

    在日常辦公中,我們經(jīng)常需要生成標(biāo)準(zhǔn)化的Word文檔,手工填充內(nèi)容不僅繁瑣,而且容易出錯(cuò),下面小編就和大家詳細(xì)講講如何使用Java根據(jù)模板生成Word文檔吧
    2026-03-03
  • 解決MultipartFile.transferTo(dest) 報(bào)FileNotFoundExcep的問題

    解決MultipartFile.transferTo(dest) 報(bào)FileNotFoundExcep的問題

    這篇文章主要介紹了解決MultipartFile.transferTo(dest) 報(bào)FileNotFoundExcep的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列示例介紹

    java數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列示例介紹

    大家好,本篇文章主要講的是java數(shù)組實(shí)現(xiàn)循環(huán)隊(duì)列示例介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • spring-boot實(shí)現(xiàn)增加自定義filter(新)

    spring-boot實(shí)現(xiàn)增加自定義filter(新)

    本篇文章主要介紹了spring-boot實(shí)現(xiàn)增加自定義filter(新),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-05-05
  • JSON for java快速入門總結(jié)學(xué)習(xí)

    JSON for java快速入門總結(jié)學(xué)習(xí)

    這篇文章主要介紹了JSON for java入門總結(jié)學(xué)習(xí),有需要的可以了解一下。
    2016-11-11
  • 深入解析spring?AOP原理及源碼

    深入解析spring?AOP原理及源碼

    這篇文章主要介紹了spring?AOP原理及源碼分析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒,需要的朋友可以參考下
    2022-04-04
  • java項(xiàng)目導(dǎo)出為.exe執(zhí)行文件的方法步驟

    java項(xiàng)目導(dǎo)出為.exe執(zhí)行文件的方法步驟

    最近做了個(gè)項(xiàng)目,想要轉(zhuǎn)換成可執(zhí)行文件,那么java項(xiàng)目如何導(dǎo)出為.exe執(zhí)行文件,本文就介紹一下,主要使用jar2exe軟件,感興趣的可以了解一下
    2021-05-05

最新評(píng)論

漯河市| 台北市| 海原县| 咸阳市| 四平市| 邓州市| 扶风县| 龙胜| 施秉县| 太和县| 汉川市| 遵义县| 东乌珠穆沁旗| 博乐市| 拜城县| 台湾省| 汉中市| 衢州市| 界首市| 五原县| 永清县| 峨山| 长顺县| 广水市| 奉新县| 金湖县| 湾仔区| 沁阳市| 金寨县| 盈江县| 古蔺县| 临高县| 青阳县| 仁怀市| 定远县| 平利县| 乌兰察布市| 武山县| 和政县| 定襄县| 珲春市|