感谢所有提示,这是我最终提出的解决方案。这比我想象的要容易。此解决方案同时使用 AspectJ 和 Annotations。它的工作原理是这样的:只需将其中一个注释(定义如下)添加到方法或类,并在开始时插入对 EDT 规则违规的简单检查。尤其是如果你像这样标记整个类,你可以只用少量的额外代码来做大量的测试。
首先我下载了AspectJ并将其添加到我的项目中(在eclipse中你可以使用AJDT)
然后我定义了两个新的注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Indicates that this class or method should only be accessed by threads
* other than the Event Dispatch Thread
* <p>
* Add this annotation to methods that perform potentially blocking operations,
* such as disk, network or database access.
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR})
public @interface WorkerThreadOnly {}
和
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Indicates that this class or method should only be accessed by the
* Event Dispatch Thread
* <p>
* Add this annotation to methods that call (swing) GUI methods
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR})
public @interface EventDispatchThreadOnly {}
之后,我定义了进行实际检查的方面:
import javax.swing.SwingUtilities;
/** Check methods / classes marked as WorkerThreadOnly or EventDispatchThreadOnly */
public aspect ThreadChecking {
/** you can adjust selection to a subset of methods / classes */
pointcut selection() : execution (* *(..));
pointcut edt() : selection() &&
(within (@EventDispatchThreadOnly *) ||
@annotation(EventDispatchThreadOnly));
pointcut worker() : selection() &&
(within (@WorkerThreadOnly *) ||
@annotation(WorkerThreadOnly));
before(): edt() {
assert (SwingUtilities.isEventDispatchThread());
}
before(): worker() {
assert (!SwingUtilities.isEventDispatchThread());
}
}
现在将@EventDispatchThreadOnly 或@WorkerThreadOnly 添加到应该是线程限制的方法或类中。不要向线程安全方法添加任何内容。
最后,只需在启用断言的情况下运行(JVM 选项 -ea),您就会很快发现违规的位置(如果有)。
为了参考,这里是Alexander Potochkin的解决方案,Mark提到的。这是一种类似的方法,但它会检查应用程序中对 swing 方法的调用,而不是应用程序内的调用。这两种方法是互补的,可以一起使用。
import javax.swing.*;
aspect EdtRuleChecker {
private boolean isStressChecking = true;
public pointcut anySwingMethods(JComponent c):
target(c) && call(* *(..));
public pointcut threadSafeMethods():
call(* repaint(..)) ||
call(* revalidate()) ||
call(* invalidate()) ||
call(* getListeners(..)) ||
call(* add*Listener(..)) ||
call(* remove*Listener(..));
//calls of any JComponent method, including subclasses
before(JComponent c): anySwingMethods(c) &&
!threadSafeMethods() &&
!within(EdtRuleChecker) {
if(!SwingUtilities.isEventDispatchThread() &&
(isStressChecking || c.isShowing()))
{
System.err.println(thisJoinPoint.getSourceLocation());
System.err.println(thisJoinPoint.getSignature());
System.err.println();
}
}
//calls of any JComponent constructor, including subclasses
before(): call(JComponent+.new(..)) {
if (isStressChecking && !SwingUtilities.isEventDispatchThread()) {
System.err.println(thisJoinPoint.getSourceLocation());
System.err.println(thisJoinPoint.getSignature() +
" *constructor*");
System.err.println();
}
}
}