基于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)的原因之一
上面添加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热加载插件/