基於JRebel開發的MybatisPlus熱加載插件


前言

前天項目中使用了mybatis-plus,但是搭配Jrebel開發項目時,發現修改mapper的xml,或者mapper方法中的注解,Jrebel並沒有能夠reload mapper.於是就有了本篇文章

探索

為了解決這個問題,首先想到的是到mybatis-plus官網查看配置方法,官網中的文檔熱加載很清楚說明了

3.0.6版本上移除了該功能,不過最新快照版已加回來並打上廢棄標識,3.1.0版本上已完全移除

按照官網配置

@Bean
@Profile("dev") // 
public MybatisMapperRefresh mybatisMapperRefresh (MybatisPlusProperties properties, SqlSessionFactory sessionFactory){
    return new MybatisMapperRefresh(properties.resolveMapperLocations(), sessionFactory, true);
}

上述配置后重新運行項目,修改mapper,發現並沒有生效,於是開始研究他的源碼。
通過查看MybatisMapperRefresh源碼發現他的實現方式:重建mapper來實現熱加載的。

XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
             sqlSessionFactory.getConfiguration(),
             resource.toString(), sqlSessionFactory.getConfiguration().getSqlFragments());
         xmlMapperBuilder.parse();

最終定位到關鍵代碼

/**
   * MybatisPlus 加載 SQL 順序:
   * <p>1、加載XML中的SQL</p>
   * <p>2、加載sqlProvider中的SQL</p>
   * <p>3、xmlSql 與 sqlProvider不能包含相同的SQL</p>
   * <p>調整后的SQL優先級:xmlSql > sqlProvider > curdSql</p>
   */
  @Override
  public void addMappedStatement(MappedStatement ms) {
      logger.debug("addMappedStatement: " + ms.getId());
      if (mappedStatements.containsKey(ms.getId())) {
          /*
           * 說明已加載了xml中的節點; 忽略mapper中的SqlProvider數據
           */
          logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");
          return;
      }
      super.addMappedStatement(ms);
  }

注:Mybatisplus重寫了mybatis的Configuration類(這也是Jrebel 無法熱加載SQL maps)的原因之一

上面添加MappedStatementConfiguration時先判斷了是否已經加載過,但是項目啟動時,Configuration已加載了所有的MappedStatement,所以MybatisMapperRefresh 這個后台線程后面reload完全沒有作用.

開搞

為了弄清楚JRebel是如何實現mybatis熱加載。

我把jrebel的插件作為libary,添加到工程里.

利用IDEA 天然的反編譯功能,順利成章的看到了源碼
下面是mybatis熱加載插件的主入口

public void preinit() {
    ClassLoader cl = MyBatisPlugin.class.getClassLoader();
    Integration integration = IntegrationFactory.getInstance();
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.io.Resources", new ResourcesCBP());
    integration.addIntegrationProcessor(cl, new String[]{"org.apache.ibatis.builder.xml.XMLConfigBuilder", "pl.atena.ibatisbaf.core.config.ConfigBuilder"}, new XMLConfigBuilderCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.session.defaults.DefaultSqlSessionFactory", new DefaultSqlSessionFactoryCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.session.Configuration", new ConfigurationCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.session.Configuration$StrictMap", new StrictMapCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.binding.MapperRegistry", new MapperRegistryCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.reflection.Reflector", new ReflectorCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.reflection.DefaultReflectorFactory", new DefaultReflectorFactoryCBP());
    integration.addIntegrationProcessor(cl, "org.mybatis.spring.SqlSessionFactoryBean", new SqlSessionFactoryBeanCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.builder.annotation.MapperAnnotationBuilder", new MapperAnnotationBuilderCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.type.TypeAliasRegistry", new TypeAliasRegistryCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.plugin.InterceptorChain", new InterceptorChainCBP());
    integration.addIntegrationProcessor(cl, "org.mybatis.spring.mapper.MapperFactoryBean", new MapperFactoryBeanCBP());
    integration.addIntegrationProcessor(cl, "org.mybatis.spring.annotation.MapperScannerRegistrar", new MapperScannerRegistrarCBP());
    integration.addIntegrationProcessor(cl, "org.apache.ibatis.binding.MapperProxy", new MapperProxyCBP());
}

JRebel在應用啟動時對mybatis的某些類做了Hook(利用Javaassit)

上述的類都是和Mapper相關的(Mapper文件解析,Mapper 注解解析…)

由於mybatis-plus重寫了mybatis的一些核心類(而JRebel的插件對mybatis中的關鍵類做了HOOK),所以導致項目中整合mybatis-plus時,修改了mapper沒有被熱加載.

為了使mybatis-plus也能夠熱加載,我想到了hook Mybatis-plus中的關鍵類,於是閱讀了mybatis-plus的源碼,整理出如下mp重寫的mybatis類。

  • MybatisConfiguration.java
  • MybatisMapperAnnotationBuilder.java
  • MybatisSqlSessionFactoryBean.java
  • MybatisMapperProxy.java

然后趁IDEA不注意的時候,去Jrebel的官網找到了開發自定義插件的文檔Custom JRebel plugins.
最終寫了這個插件。

PS:其中大部分的代碼來自原插件反編譯后代碼,同時結合Mybatis-plus重寫的源碼,做了相應適配.

下面是插件源碼地址:

jrebel-mybatisplus

下面是插件下載地址:

jr-mybatisplus.zip

如何使用請閱讀README.md

使用

將下載好的插件jr-mybatisplus.zip解壓並拷貝至任意目錄, 比如: d:\jrebel\plugin\jr-mybatisplus.jar

打開你的IDE(Intellij IDEA or Eclipse),修改運行配置,增加VM參數:-Drebel.plugins=d:\jrebel\plugin\jr-mybatisplus.jar,然后以JRebel方式啟動

檢查插件是否生效:

修改你項目中的mapper xml 文件后,重新編譯,如果重新請求接口,你應該會看到控制台輸出 “Reloading SQL maps”

總結

    • 學習了javaassit的使用.

    • 閱讀了mybatis && mybatis-plus 的源碼,了解到mybatis的實現原理,以及mybatis-plus的相關原理.

    • 學習了JRebel的插件開發方式以及它的Hot Reloading原理.

 文章來源:https://githuboy.online/2019/05/11/基於JRebel開發的MybatisPlus熱加載插件/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM