前言
前天項目中使用了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)的原因之一
上面添加MappedStatement
到Configuration
時先判斷了是否已經加載過,但是項目啟動時,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重寫的源碼,做了相應適配.
下面是插件源碼地址:
下面是插件下載地址:
如何使用請閱讀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熱加載插件/