清单 3. 实施 Swing 的单线程规则的方面
public aspect SwingThreadAspect {
pointcut swingMethods() : call(* javax.swing..*.*(..))
|| call(javax.swing..*.new(..));
pointcut extendsSwing() : call(* javax.swing.JComponent+.*(..))
|| call(* javax.swing..*Model+.*(..))
|| call(* javax.swing.text.Document+.*(..));
pointcut safeMethods() : call(void JComponent.revalidate())
|| call(void JComponent.invalidate(..))
|| call(void JComponent.repaint(..))
|| call(void add*Listener(EventListener+))
|| call(void remove*Listener(EventListener+))
|| call(boolean SwingUtilities.isEventDispatchThread())
|| call(void SwingUtilities.invokeLater(Runnable))
|| call(void SwingUtilities.invokeAndWait(Runnable))
|| call(void JTextPane.replaceSelection(..))
|| call(void JTextPane.insertComponent(..))
|| call(void JTextPane.insertIcon(..))
|| call(void JTextPane.setLogicalStyle(..))
|| call(void JTextPane.setCharacterAttributes(..))
|| call(void JTextPane.setParagraphAttributes(..));
pointcut edtMethods() : (swingMethods() || extendsSwing()) && !safeMethods();
before() : edtMethods() {
if (!SwingUtilities.isEventDispatchThread())
throw new AssertionError(thisJoinPointStaticPart.getSignature()
+ " called from " + Thread.currentThread().getName());
}
}
|
swingMethods() 切入点包含对 javax.swing 包中的所有方法(包括构造函数)的调用。extendsSwing() 切入点代表对所有扩展自 JComponent 或任何 Swing 模型类的类中方法的全部调用。safeMethods() 切入点代表一些已知可以从任何线程安全调用的 Swing 方法。
SwingThreadAspect 并不完美,但是足够了。safeMethods() 切入点没有完全枚举线程安全方法,而且 extendsSwing() 切入点可能也没有包含所有经常被扩展的 Swing 类。但是我们不会把它们用于生产 —— 只是用它们进行测试。它能够不必为每个程序创建新的测试用例就发现 bug,而这就是它的价值所在。而且,像大多数 bug 探测器一样,它可能会在以前以为是正确的程序中找到 bug。









