<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mybatis 插件加載原理與實(shí)戰(zhàn)

          共 5679字,需瀏覽 12分鐘

           ·

          2021-07-15 19:33

          點(diǎn)擊上方「Java有貨」關(guān)注我們


          技術(shù)交流群添加方式


          +



          添加小編微信:372787553,備注:進(jìn)群
          帶您進(jìn)入Java技術(shù)交流群

          導(dǎo)語



          想知道Mybatis的插件是如何生效的就需要了解mybatis的配置,所有的信息都在Mybatis Configuration內(nèi)部,

          在之前的文章中,我們也會看到或如下的代碼:

          public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);  // 加載插件  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  return parameterHandler;}


          Mybatis 配置文件概覽



          MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設(shè)置和屬性信息。配置文檔的頂層結(jié)構(gòu)如下:

          • configuration(配置)

            • environment(環(huán)境變量)

            • transactionManager(事務(wù)管理器)

            • dataSource(數(shù)據(jù)源)

            • properties(屬性)

            • settings(設(shè)置)

            • typeAliases(類型別名)

            • typeHandlers(類型處理器)

            • objectFactory(對象工廠)

            • plugins(插件)

            • environments(環(huán)境配置)

            • databaseIdProvider(數(shù)據(jù)庫廠商標(biāo)識)

            • mappers(映射器)


          InterceptorChain




          看了Mybatis的源碼我們發(fā)現(xiàn),加載插件其實(shí)是通過InterceptorChain進(jìn)行加載的;

          public class InterceptorChain {
          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);}
          }

          InterceptorChain 內(nèi)部維護(hù)了一個集合Interceptor(Interceptor 是一個接口), 然后通過 pluginAll 進(jìn)行循環(huán)加載;


          插件(plugins)


          MyBatis 允許你在映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括:

          • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

          • ParameterHandler (getParameterObject, setParameters)

          • ResultSetHandler (handleResultSets, handleOutputParameters)

          • StatementHandler (prepare, parameterize, batch, update, query)

          這些類中方法的細(xì)節(jié)可以通過查看每個方法的簽名來發(fā)現(xiàn),或者直接查看 MyBatis 發(fā)行包中的源代碼。如果你想做的不僅僅是監(jiān)控方法的調(diào)用,那么你最好相當(dāng)了解要重寫的方法的行為。因?yàn)樵谠噲D修改或重寫已有方法的行為時,很可能會破壞 MyBatis 的核心模塊。這些都是更底層的類和方法,所以使用插件的時候要特別當(dāng)心。

          通過 MyBatis 提供的強(qiáng)大機(jī)制,使用插件是非常簡單的,只需實(shí)現(xiàn) Interceptor 接口,并指定想要攔截的方法簽名即可。

          如果您不確定攔截了哪些對象,可以在 Configuration 搜素 pluginAll ;


          自定義Mybatis 插件


          在前面我們說過,插件其本質(zhì)就是攔截器的原理 ,那么我們實(shí)現(xiàn)Interceptor 接口既可以;


          @Intercepts({ @Signature(type = org.apache.ibatis.executor.Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class ExamplePlugin implements Interceptor { private Properties properties = new Properties();
          @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("自定義攔截器已經(jīng)被執(zhí)行....."); Object returnObject = invocation.proceed(); // 必要時進(jìn)行后期處理 return returnObject; }
          @Override public void setProperties(Properties properties) { this.properties = properties; }}
          // 可在在其中獲取哪些值/** * 獲取被攔截的目前類,在這里是攔截了statementHandler,所有目前類也就是它 * 通過這個類我們可以拿到待執(zhí)行的sql語句,通常使用mataObject工具類來獲取 * 關(guān)于這個工具類,大家可自行了解,個人認(rèn)為這個工具類很強(qiáng)大 */ StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); /** * 先解釋下為什么寫成delegate.boundSql就可以拿到boundSql類 * 從前面也可以得知,statementHandler的默認(rèn)實(shí)現(xiàn)是routingStatementHandler。 * 這個類有一個屬性statementHandler,屬性名就叫delegate,而這個屬性的默認(rèn)實(shí)現(xiàn)又是preparedStatementHandler * 后面這個類又有屬性boundSql,所以,最終形成的寫法就是delegate.boundSql。 * 所以這也體現(xiàn)了MetaObject工具類的強(qiáng)大,可以通過實(shí)例傳參,就可以根據(jù)屬性名獲取對應(yīng)屬性值 */ BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
          // 待執(zhí)行的sql,在這里也就是預(yù)編譯后的sql,即參數(shù)位都是?號 String sql = boundSql.getSql(); /** * 既然拿到了預(yù)編譯后的sql,那就可以按照你自己的想法為所欲為,如分頁,按年分表等等 * 分表的話,個人推薦druid的sql解析器,我認(rèn)為還是不錯的,大家可以自行了解 * 最后改造完sql,別忘了把它設(shè)置回去 * metaObject.setValue("delegate.boundSql.sql",sql); * invocation.proceed,即原始方法的執(zhí)行 * 注意點(diǎn)就是,因?yàn)閙ybatis插件采用的是代理模式,所以如果存在多個插件,會形成多個代理 * 你如果要拿到最原始的對象,還得進(jìn)一步進(jìn)行分解 * 如:while(metaObject.getValue(""h) != null){ * Object obj = metaObject.getValue("h"); * .... * } */ return invocation.proceed();

          配置插件

          • XML配置

            <!-- mybatis-config.xml -->
            <plugins>
             <plugin interceptor="com.javayh.mybatis.config.ExamplePlugin">
               <property name="someProperty" value="100"/>
             </plugin>
            </plugins>
          • java配置類

            @org.springframework.context.annotation.Configurationpublic class MapperConfig {
            //注冊插件 @Bean public ExamplePlugin myPlugin() { ExamplePlugin myPlugin = new ExamplePlugin(); //設(shè)置參數(shù),比如閾值等,可以在配置文件中配置,這里直接寫死便于測試 Properties properties = new Properties(); //這里設(shè)置慢查詢閾值為1毫秒,便于測試 properties.setProperty("time", "1"); myPlugin.setProperties(properties); return myPlugin; } //將插件加入到mybatis插件攔截鏈中 /*@Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { //插件攔截鏈采用了責(zé)任鏈模式,執(zhí)行順序和加入連接鏈的順序有關(guān) ExamplePlugin myPlugin = new ExamplePlugin(); //設(shè)置參數(shù),比如閾值等,可以在配置文件中配置,這里直接寫死便于測試 Properties properties = new Properties(); //這里設(shè)置慢查詢閾值為1毫秒,便于測試 properties.setProperty("time", "1"); myPlugin.setProperties(properties); configuration.addInterceptor(myPlugin); }; }*/}

          上面的插件將會攔截在 Executor 實(shí)例中所有的 “update 、query” 方法調(diào)用, 這里的 Executor 是負(fù)責(zé)執(zhí)行底層映射語句的內(nèi)部對象。

          提示 覆蓋配置類

          除了用插件來修改 MyBatis 核心行為以外,還可以通過完全覆蓋配置類來達(dá)到目的。只需繼承配置類后覆蓋其中的某個方法,再把它傳遞到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,這可能會極大影響 MyBatis 的行為,務(wù)請慎之又慎。

          瀏覽 61
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久麻豆 | 国产激情视频在线 | 操逼操欧美 | 五月丁香俺也去国产 | www.啪啪啪免费视频 |