清单 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。