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

前言

前天項目中使用了mybatis-plus,可是搭配Jrebel開發項目時,發現修改mapper的xml,或者mapper方法中的註解,Jrebel並無可以reload mapper.因而就有了本篇文章html

探索

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

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

按照官網配置github

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

上述配置後從新運行項目,修改mapper,發現並無生效,因而開始研究他的源碼。
經過查看MybatisMapperRefresh源碼發現他的實現方式:重建mapper來實現熱加載的。spring

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

最終定位到關鍵代碼sql

/**
   * MybatisPlus 加載 SQL 順序:
   * <p>一、加載XML中的SQL</p>
   * <p>二、加載sqlProvider中的SQL</p>
   * <p>三、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)的緣由之一apache

上面添加MappedStatementConfiguration時先判斷了是否已經加載過,可是項目啓動時,Configuration已加載了全部的MappedStatement,因此MybatisMapperRefresh 這個後臺線程後面reload徹底沒有做用.session

開搞

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

我把jrebel的插件做爲libary,添加到工程裏.app

利用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熱加載插件/

相關文章
相關標籤/搜索