From 54d20cdf2818a8e628aaeb1aff82a94c67bc565f Mon Sep 17 00:00:00 2001 From: lyf <78062919@qq.com> Date: Thu, 10 Jan 2019 10:44:51 +0800 Subject: [PATCH] =?UTF-8?q?=E7=83=AD=E5=8A=A0=E8=BD=BD=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=B1=BB=E5=A2=9E=E5=8A=A0=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E5=88=86=E6=9E=90=E6=96=B9=E6=B3=95=EF=BC=9A?= =?UTF-8?q?=20=E5=B9=B6=E9=9D=9E=E6=89=80=E6=9C=89IDE=20=E7=9A=84=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=BC=96=E8=AF=91=E5=8A=9F=E8=83=BD=E9=83=BD=E6=98=AF?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=AF=B9class=E6=96=87=E4=BB=B6=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E4=BF=AE=E6=94=B9=E6=93=8D=E4=BD=9C=EF=BC=8C=E4=BE=8B?= =?UTF-8?q?=E5=A6=82=20IntelliJ=20IDEA=20=E5=B0=B1=E4=BC=9A=E9=92=88?= =?UTF-8?q?=E5=AF=B9=E5=8F=98=E5=8C=96=E7=9A=84class=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=85=88=E8=BF=9B=E8=A1=8C=E5=88=A0=E9=99=A4=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=EF=BC=8C=20=E7=84=B6=E5=90=8E=E5=86=8D=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=96=B0=E7=9A=84class=E6=96=87=E4=BB=B6=EF=BC=8C=E7=84=B6?= =?UTF-8?q?=E5=90=8E=E5=AF=B9=E6=96=B0=E5=88=9B=E5=BB=BA=E7=9A=84class?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=BF=9B=E8=A1=8C=E4=BF=AE=E6=94=B9=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=EF=BC=8C=E5=9B=A0=E6=AD=A4=E4=BC=9A=E4=BA=A7=E7=94=9F?= =?UTF-8?q?=E4=B8=80=E7=B3=BB=E5=88=97=E7=B3=BB=E7=BB=9F=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8F=98=E5=8C=96=E4=BA=8B=E4=BB=B6=EF=BC=8C=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=EF=BC=9A=201=E3=80=81class=E6=96=87=E4=BB=B6=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=202=E3=80=81class=E6=96=87=E4=BB=B6=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=BA=8B=E4=BB=B6=203=E3=80=81class=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=89=80=E5=9C=A8=E7=9B=AE=E5=BD=95=E4=BF=AE=E6=94=B9=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=204=E3=80=81class=E6=96=87=E4=BB=B6=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=205=E3=80=81class=E6=96=87=E4=BB=B6=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=BA=8B=E4=BB=B6=EF=BC=88=E7=9C=9F=E6=AD=A3=E5=86=99?= =?UTF-8?q?=E5=85=A5class=E6=96=87=E4=BB=B6=E5=86=85=E5=AE=B9=EF=BC=89=206?= =?UTF-8?q?=E3=80=81class=E6=96=87=E4=BB=B6=E6=89=80=E5=9C=A8=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E4=BF=AE=E6=94=B9=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为了兼容这类IDE,并不能在监听到class文件修改事件后便立即进行热加载过程,因为有些修改事件实际上是无效事件,比如上述事件1,虽然 是class文件修改事件,但是立马又产生了class文件删除事件将class文件删除了,此时触发热加载,将会引起class文件不存在异常。 因此当捕获到class文件修改事件后,不立即进行热加载,而是继续观察数毫秒,判断是否还有后续删除事件,如果有则视为此次修改事件等同于删除事件不 进行热加载;如果没有才认为是真实修改行为,此时才进行热加载操作。 --- .../undertow/hotswap/HotSwapWatcher.java | 84 +++++++++++++++---- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java b/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java index 248c3ca..551d75e 100644 --- a/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java +++ b/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java @@ -30,6 +30,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; + import com.jfinal.server.undertow.UndertowKit; import com.jfinal.server.undertow.UndertowServer; @@ -42,7 +44,8 @@ public class HotSwapWatcher extends Thread { // protected int watchingInterval = 1000; // 1900 与 2000 相对灵敏 protected int watchingInterval = 500; - + protected int guessInterval = 3; //部分IDE会在class文件修改事件发生后1-2ms内删除class文件 + protected List watchingPaths; private WatchKey watchKey; protected volatile boolean running = true; @@ -93,7 +96,8 @@ public class HotSwapWatcher extends Thread { throw new RuntimeException(e); } } - + + protected void doRun() throws IOException { WatchService watcher = FileSystems.getDefault().newWatchService(); addShutdownHook(watcher); @@ -101,7 +105,7 @@ public class HotSwapWatcher extends Thread { for (Path path : watchingPaths) { path.register( watcher, - // StandardWatchEventKinds.ENTRY_DELETE, + StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE ); @@ -124,29 +128,73 @@ public class HotSwapWatcher extends Thread { } break ; } - + List> watchEvents = watchKey.pollEvents(); - for(WatchEvent event : watchEvents) { - String fileName = event.context().toString(); - if (fileName.endsWith(".class")) { - if (undertowServer.isStarted()) { - undertowServer.restart(); - resetWatchKey(); - - while((watchKey = watcher.poll()) != null) { - // System.out.println("---> poll() "); - watchKey.pollEvents(); - resetWatchKey(); - } - - break ; + Set changeFiles = new HashSet<>(); + analyzeChangeFiles(changeFiles, watchEvents); + + + if(!changeFiles.isEmpty()){ + try { + resetWatchKey(); + //部分IDE会在class文件修改后1-2ms删除文件,并创建新class文件 + if((watchKey = watcher.poll(guessInterval, TimeUnit.MILLISECONDS)) != null){ + List> guessEvents = watchKey.pollEvents(); + analyzeChangeFiles(changeFiles, guessEvents); } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if ( !changeFiles.isEmpty() && undertowServer.isStarted()) { + undertowServer.restart(); + resetWatchKey(); + + while((watchKey = watcher.poll()) != null) { + // System.out.println("---> poll() "); + watchKey.pollEvents(); + resetWatchKey(); } + } resetWatchKey(); } } + + /** + * 此方法用于辅助判断真实修改行为 + * 并非所有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文件修改事件后,不立即进行热加载,而是继续观察数毫秒,判断是否还有后续删除事件,如果有则视为此次修改事件等同于删除事件不 + * 进行热加载;如果没有才认为是真实修改行为,此时才进行热加载操作。 + * @param changeFiles 记录一次事件中发生变化的文件 + * @param watchEvents 监听到的文件变化事件List + * @return void + */ + private void analyzeChangeFiles(Set changeFiles, List> watchEvents){ + for(WatchEvent event : watchEvents) { + String fileName = event.context().toString(); + WatchEvent.Kind kind = event.kind(); + if (fileName.endsWith(".class")) { + if(kind == StandardWatchEventKinds.ENTRY_DELETE){ + changeFiles.remove(fileName); + }else{ + changeFiles.add(fileName); + } + } + } + } private void resetWatchKey() { if (watchKey != null) { -- Gitee