清单 1. 执行 “不调用 System.gc()” 规则的动态方面
public aspect GcAspect { pointcut gcCalls() : call(void java.lang.System.gc()); before() : gcCalls() { throw new AssertionError("Don't call System.gc!"); } } |
System.gc()
的调用,而不是程序只需包含一个对 System.gc()
的调用,就会被探测到。但是,很快就会看到,动态方面更灵活,因为它们能在方面触发的点上执行任意测试代码,从而对声明的问题提供更精细的控制。
也可以容易地创建一个静态方面,在编译时识别对 System.gc()
的调用,如清单 2 所示。同样,如果想发现在库代码中出现的这个 bug 模式,不仅要处理项目中的代码,还要处理它使用的库。
清单 2. 执行 “不调用 System.gc()” 规则的静态方面
public aspect StaticGcAspect { pointcut gcCalls() : call(void java.lang.System.gc()); declare error : gcCalls() : "Don't call System.gc!"; } |
有一个几乎无法静态地实施的规则是线程限制 —— 指定的对象只能从一个线程访问(有时是特定线程,例如 Swing 事件线程)。Swing 程序的正确性依赖于线程限制,但是对于实施这个规则,从编译器、运行时或类库都得不到任何帮助。如果违犯了这个规则,程序就会被破坏,但是因为在测试时程序可能看起来工作正常,所以问题可能一直暴露不了。
Swing 单线线程规则指定:
Swing 组件和模块只应当从事件分派线程中创建、修改和实现。
SwingUtilities.isEventDispatchThread()
方法,Swing 提供了一个机制,询问 “当前线程是不是事件分派线程?”。所以需要在每个 Swing 的方法调用之前插入代码,检查调用是否是由合适的线程发出的,如果不是由合适线程发出的,就抛出 AssertionError
,这样就能在测试中捕捉到对单线程规则的违犯,而不会让它们在生产中造成莫名其妙的故障。