From 808f940df284f1f95e0d4550383f65dedba6ec64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=2EYing-=E5=BA=94=E5=BF=97=E6=B5=A9?= Date: Fri, 14 Apr 2023 13:41:06 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-83195=20=E5=8D=A1=E9=A1=BF=E7=82=B9?= =?UTF-8?q?=E4=BC=98=E5=8C=96-=E5=BC=80=E5=8F=91=E8=80=85=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/carton/SwitchForSwingChecker.java | 74 ++++++++- .../design/carton/developer/AwtEventInfo.java | 73 +++++++++ .../developer/EventDispatchDeveloperMode.java | 146 ++++++++++++++++++ 3 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/design/carton/developer/AwtEventInfo.java create mode 100644 designer-base/src/main/java/com/fr/design/carton/developer/EventDispatchDeveloperMode.java diff --git a/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java b/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java index 683179293e..837bdb1c8b 100644 --- a/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java +++ b/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java @@ -1,6 +1,7 @@ package com.fr.design.carton; +import com.fr.design.carton.developer.EventDispatchDeveloperMode; import com.fr.design.i18n.Toolkit; import com.fr.general.GeneralUtils; import com.fr.json.JSON; @@ -29,11 +30,22 @@ import java.util.Map; import java.util.Date; import java.util.Calendar; + public class SwitchForSwingChecker implements XMLReadable, XMLWriter { /** * Designer4Debug类名 */ private static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug"; + + /** + * 真正主类 + */ + private static final String NORMAL_MAIN_CLASS_NAME = "com.fr.start.MainDesigner"; + + /** + * 开发者模式jvm参数 + */ + private static final String CARTON_DEVELOPER_JVM_PARAM = "cartonDeveloper"; /** * XML标签 */ @@ -249,11 +261,7 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter { */ public static void initThreadMonitoring () { String mainClass = System.getProperty("sun.java.command"); - //判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用 - if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) { - EventDispatchThreadHangMonitor.initMonitoring(); - AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); - } + MainClassType.getMainClassType(mainClass).initEventQueue(); } /** @@ -309,4 +317,60 @@ public class SwitchForSwingChecker implements XMLReadable, XMLWriter { writer.end(); } + /** + * 根据程序启动类路径进行区分 + * + * @author John.Ying + * @since 11.0 + * Created on 2023/4/14 + */ + enum MainClassType { + /** + * 用mainDesigner启动 + */ + MAIN(NORMAL_MAIN_CLASS_NAME) { + @Override + void initEventQueue() { + if (StringUtils.equals("true", System.getProperty(CARTON_DEVELOPER_JVM_PARAM))) { + EventDispatchDeveloperMode.INSTANCE.initMonitoring(); + } else { + EventDispatchThreadHangMonitor.initMonitoring(); + AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); + } + } + }, + + /** + * designer4debug启动 + */ + DEBUG_MAIN(DEBUG_MAIN_CLASS_NAME) { + @Override + void initEventQueue() { + + } + }; + + MainClassType(String classPath) { + this.classPath = classPath; + } + + String classPath; + + /** + * 初始化重写的EDT + */ + abstract void initEventQueue(); + + /** + * @param classPath 启动类路径 + */ + static MainClassType getMainClassType(String classPath) { + for (MainClassType mainClassType : MainClassType.values()) { + if (StringUtils.equals(classPath, mainClassType.classPath)) { + return mainClassType; + } + } + return MAIN; + } + } } diff --git a/designer-base/src/main/java/com/fr/design/carton/developer/AwtEventInfo.java b/designer-base/src/main/java/com/fr/design/carton/developer/AwtEventInfo.java new file mode 100644 index 0000000000..77274a36ab --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/developer/AwtEventInfo.java @@ -0,0 +1,73 @@ +package com.fr.design.carton.developer; + +import java.awt.AWTEvent; + +/** + * EDT事件的包装类,用来额外处理信息 + * + * @author John.Ying + * @since 11.0 + * Created on 2023/4/14 + */ +public class AwtEventInfo { + //获取执行该事件的线程 + private final Thread eventDispatchThread = Thread.currentThread(); + //在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间 + private long lastDispatchTimeMillis = System.currentTimeMillis(); + //事件开始的时间 + private final long startDispatchTimeMillis = System.currentTimeMillis(); + //awt事件 + private AWTEvent awtEvent; + //事件堆栈 + private StackTraceElement[] stackTrace; + + /** + * 检查是否要给堆栈赋值 + */ + public void checkForHang() { + if (isNeedToStackTrace()) { + this.stackTrace = eventDispatchThread.getStackTrace(); + } + } + + /** + * 是否需要赋值堆栈满足 + * 耗时>20ms + */ + private boolean isNeedToStackTrace() { + return (System.currentTimeMillis() - startDispatchTimeMillis) > EventDispatchDeveloperMode.MAX_TIME + && stackTrace == null; + } + + public Thread getEventDispatchThread() { + return eventDispatchThread; + } + + public long getLastDispatchTimeMillis() { + return lastDispatchTimeMillis; + } + + public void setLastDispatchTimeMillis(long lastDispatchTimeMillis) { + this.lastDispatchTimeMillis = lastDispatchTimeMillis; + } + + public long getStartDispatchTimeMillis() { + return startDispatchTimeMillis; + } + + public AWTEvent getAwtEvent() { + return awtEvent; + } + + public void setAwtEvent(AWTEvent awtEvent) { + this.awtEvent = awtEvent; + } + + public StackTraceElement[] getStackTrace() { + return stackTrace; + } + + public void setStackTrace(StackTraceElement[] stackTrace) { + this.stackTrace = stackTrace; + } +} diff --git a/designer-base/src/main/java/com/fr/design/carton/developer/EventDispatchDeveloperMode.java b/designer-base/src/main/java/com/fr/design/carton/developer/EventDispatchDeveloperMode.java new file mode 100644 index 0000000000..4fd136da37 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/developer/EventDispatchDeveloperMode.java @@ -0,0 +1,146 @@ +package com.fr.design.carton.developer; + + +import com.fr.concurrent.FineExecutors; +import com.fr.design.carton.EventDispatchThreadHangMonitor; +import com.fr.design.ui.util.UIUtil; +import com.fr.log.FineLoggerFactory; + +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 开发者模式重写的EDT + * + * @author John.Ying + * @since 11.0 + * Created on 2023/4/14 + */ +public final class EventDispatchDeveloperMode extends EventQueue { + + /** + * 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove + */ + private final LinkedList dispatches = new LinkedList<>(); + /** + * 开启间隔检测后两次检测的相隔时间ms + */ + public static final long CHECK_INTERVAL_MS = 5; + + private Timer timer; + + /** + * edt事件最大允许的时间 + */ + public static final long MAX_TIME = 20; + public static final EventDispatchDeveloperMode INSTANCE = new EventDispatchDeveloperMode(); + + @Override + protected void dispatchEvent(AWTEvent event) { + try { + preDispatchEvent(event); + super.dispatchEvent(event); + } finally { + postDispatchEvent(); + } + } + + /** + * 事件分发前处理 + */ + private synchronized void preDispatchEvent(AWTEvent event) { + synchronized (dispatches) { + AwtEventInfo awtEventInfo = new AwtEventInfo(); + awtEventInfo.setAwtEvent(event); + dispatches.addLast(awtEventInfo); + } + } + + /** + * 事件分发后处理 + */ + private synchronized void postDispatchEvent() { + synchronized (dispatches) { + AwtEventInfo awtEventInfo = dispatches.removeLast(); + //嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis + Thread currentEventDispatchThread = Thread.currentThread(); + for (AwtEventInfo info : dispatches) { + info.setLastDispatchTimeMillis(System.currentTimeMillis()); + } + long nowTime = System.currentTimeMillis(); + long totalTime = nowTime - awtEventInfo.getStartDispatchTimeMillis(); + long continuationTime = nowTime - awtEventInfo.getLastDispatchTimeMillis(); + if (continuationTime > MAX_TIME) { + FineLoggerFactory.getLogger() + .warn("awt event spend time more than 20ms, totalTime {} continuationTime {} the stack is {}" + , totalTime, continuationTime, EventDispatchThreadHangMonitor.stackTraceToStringForConsole(awtEventInfo.getStackTrace())); + } + } + } + + /** + * 将swing中默认的EventQueue换成自己的 + */ + public void initMonitoring() { + UIUtil.invokeLaterIfNeeded(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE)); + initTimer(); + startFilterModalWindow(); + } + + /** + * Sets up a timer to check for hangs frequently. + * 初始化一个Timer + */ + public void initTimer() { + final long initialDelayMs = 0; + final boolean daemon = true; + timer = new Timer("EventDispatchDeveloperMode", daemon); + timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS); + } + + /** + * /消除Timer + */ + public void stopTimer() { + if (timer != null) { + timer.cancel(); + } + } + + /** + * /定时执行的任务 + */ + public class HangChecker extends TimerTask { + @Override + public void run() { + synchronized (dispatches) { + //如果链表为空定时检测就不进行 + if (dispatches.isEmpty()) { + return; + } + dispatches.getLast().checkForHang(); + } + } + } + + /** + * 消除模态框影响 + */ + public void startFilterModalWindow() { + ScheduledExecutorService scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(); + scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + UIUtil.invokeLaterIfNeeded(() -> { + //不用干事,切个片就可以 + }); + } + }, 0, 10, TimeUnit.MILLISECONDS); + } +}