jfinal-undertow 在IntelliJ IDEA中使用报错

IntelliJ IDEA下开启jfinal-undertow的热加载功能,报错提示找不到修改的文件对应的类:

IntelliJ IDEA配置:

image.png

image.png

image.png


image.png

评论区

JFinal

2019-01-07 23:40

添加配置:
undertow.hotSwapClassPrefix=com.jjiehao.

这个在文档中有过说明

lyf78062919

2019-01-09 09:20

@JFinal 这个我已经配置过了,我遇到的问题跟配置无关,通过看JFinal-undertow源码找到了原因,是因为IntelliJ IDEA在自动生成class文件时候有延时,导致com.jfinal.server.undertow.hotswap.HotSwapClassLoader加载新class文件时抛出未找到异常,我在HotSwapClassLoader 类中加入重试机制,已经解决了这个问题。

lyf78062919

2019-01-09 09:29

@JFinal 猜想可能是IntelliJ IDEA在生成新的class文件后没有立即释放文件资源,导致虽然触发了系统的文件修改事件,但是class文件还不可读,导致HotSwapClassLoader加载不到,稍微延迟数百ms后便可以正确加载。

JFinal

2019-01-09 10:43

@lyf78062919 这是一个很好的探索,将你的改进代码分享出来啊

lyf78062919

2019-01-09 11:13

@JFinal 我已经在码云上提交pull requests啦

JFinal

2019-01-09 11:21

@lyf78062919 刚刚看到了, class loader 中重试这个不太好,性能会有所影响

有些类在 HotSwapClassLoader 中无法加载的类,需要尽快抛出异常,让其它的 class loader 来加载,这种情况就会拖慢性能,虽说一次时间不多,但累积起来会很可观

得再找别的解决办法

最后,貌似这个问题只有你碰到了

lyf78062919

2019-01-09 11:25

@JFinal 确实针对其他正常加载不到的情况会影响性能,我再考虑下,同时我也在研究IntelliJ IDEA的automake机制,看为什么我这边会出现

JFinal

2019-01-09 11:26

@lyf78062919 或许是 IDEA 在 automake 时会先删掉 target/classes 下所有资源,然后再一次性生成,这个就有点慢

eclipse 的自动编译就不是上面的处理方式

JFinal

2019-01-09 11:28

@lyf78062919 对了,修改一下本贴子,将你开启 IDEA 的 auto make 方式贴上来,或许是这里出的问题

这里也有一些相关的资源:
https://www.jfinal.com/doc/1-5

lyf78062919

2019-01-09 11:35

@JFinal 这个我之前也考虑过,但是通过观察文件系统时间判断,IDE只是针对修改后的文件重新生成的class

lyf78062919

2019-01-10 10:58

@JFinal 通过研究IntelliJ IDEA的自动编译机制找到问题了根源了:
并非所有IDE 的自动编译功能都是直接对class文件进行修改操作,例如 IntelliJ IDEA 就会针对变化的class文件先进行删除操作,再创建新的class文件,然后对新创建的class文件进行修改操作,因此会产生一系列系统文件变化事件,如下:
1、class文件修改事件
2、class文件删除事件
3、class文件所在目录修改事件
4、class文件创建事件
5、class文件修改事件(真正写入class文件内容)
6、class文件所在目录修改事件

为了兼容这类IDE,不在监听到class文件修改事件后便立即进行热加载过程,因为有些修改事件实际上是无效事件,比如上述事件1,虽然是class文件修改事件,但是立马又产生了class文件删除事件将class文件删除了,此时触发热加载,将会引起class文件不存在异常。
因此当捕获到class文件修改事件后,不立即进行热加载,而是继续观察数毫秒,判断是否还有后续删除事件,如果有则视为此次修改事件等同于删除事件不进行热加载;如果没有才认为是真实修改行为,此时才进行热加载操作。

此前通过对HotSwapClassLoader类的改造方式比较丑陋粗暴,而且会影响性能,现通过分析清楚问题根源后从监控事件入手,在原因监听操作基础上只增加了一步真实修改行为分析操作,在不影响性能的情况下实现了对怪异IDE行为的兼容。经测试IntelliJ IDEA、Eclipse下目前都正常。

JFinal

2019-01-10 12:11

@lyf78062919 看到你的这个分析,这个情况应该是 IDEA 进行的是 build 动作,而不是 make 动作, make 与 build 是有很大差别的:make 只编译改变过的源代码,builder 是对整个源码进行编译

此外,还有一个 compile , 这个是对指定的源代码进行编译,无论源代码有没有被个改过

因此,在开发阶段,最划算最合理的是要实现 auto make,或者叫 auto compile 也可以,build 动作会删除所有被编译的 class 文件然后编译所有源码

JFinal

2019-01-10 12:14

你 pull 的代码不能直接合并,这段代码极为精密,细节极多。 你的改进我会先分析,找出一个最好的方案后再改进

谢谢你的支持

lyf78062919

2019-01-10 12:26

@JFinal 好的,我这边开发暂时这样使用,期待你那边的更新

JFinal

2019-01-10 12:26

目前我建议你先创建一个 MyServer extends UndertowServer,覆盖掉 doStart() 方法,然后用上你自己改进的这个 HotSwapWatcher

可以先建一个 MyHotSwapWatcher extends HotSwapWatcher ,覆盖掉其中的相关方法进行改造

lyf78062919

2019-01-10 12:27

@JFinal 可行,我这边项目先采用这种方式

lyf78062919

2019-01-10 12:51

@JFinal 顺便分享下心得为后面的同学铺路,关于 IDEA 的到底进行的是make、compile 、还是build。
1、IDEA 默认是没有提供像 Eclipse 的实时自动编译功能,但是可以通过ctrl+shift+A进入Registry修改配置实现,compiler.automake.allow.when.app.running配置的官方配置说明是:Allow auto-make to start even if developed application is currently running. Note that automatically started make may eventually delete some classes that are required by the application,意思是说:允许在应用运行时开启auto-make功能,但是最终可能会删除一些应用需要的class文件。
2、通过观察每次修改文件触发 IDEA 自动编译,检测target目录中所有class文件状态,可见只有对应修改后的class文件状态发生了变化。
3、通过捕获 IDEA 自动编译时期文件系统的所有状态事件,只有对应修改的class。

综上我的结论是: IDEA 的模式应该还是属于make,只有在java文件变化了才会触发,而且也只针对变化的文件而并非全项目build。但是比较奇葩的是,它不是直接修改对应class文件,而且先删除再创建再修改。( 以上分析基于 IDEA 2018.2.6版本 )

JFinal

2019-01-10 15:29

@lyf78062919 这个认知十分深入,从来没有人这么有耐心的研究过这个问题,贴子已收藏,下次有同学需要直接给贴子链接,谢谢

l745230

2019-02-26 19:26

前人种树后人乘凉,同遇到此问题,按回复中的方法修改尝试了下,完美解决BlogController修改热加载报错的问题。IDEA中的热加载还挺好用,比起jrebel又简单又方便

chcode

2019-06-29 13:03

我是直接 Thread.sleep(1000); 然后undertowServer.restart();然后解决了

JFinal

2019-06-29 13:56

@chcode Thread.sleep(1000) 是加在哪个地方的? 能否分享出来,有助于 jfinal undertow 后面的改进

chcode

2019-06-29 18:33

自己定义MyHotSwapWatcher继承HotSwapWatcher覆盖doRun方法,在方法内的undertowServer.restart();前面加上Thread.sleep(1000) ;

chcode

2019-06-29 18:42

我还可以提供一个思路,监控.class文件完成更改后再进行热加载,这样就不会出现java.lang.NoClassDefFoundError,由于jdk 的watcherService并不能判断文件什么时候写入完毕可以,使用file.rename()方法去判断文件是否完成更改,在windows下面文件正在变更的文件无法去rename,重命名文件名和原文件名一样,但是此方法无法在linux系统下使用

JFinal

2019-06-30 11:35

@chcode 谢谢你的建议, Thread.sleep(1000) 时间或许有点长,参数改成 500 是否在你的机器上可行?

再一个,即便是出现错误,会不会在后面继续刷新后,错误将不再出现

冰雨

2019-07-04 10:54

@lyf78062919 这种研究探索精神令人敬佩,点赞加收藏!向你学习!

热门反馈

扫码入社