\apps\FindBugs-0.7.3"/>
可选属性output指定FindBugs的结果使用的输出格式。
可能的值有xml、text或者emacs。
如果没有指定outputFile,那么FindBugs会使用标准输出。
如前所述,XML格式有可以在UI中观看的额外好处。
第3行:
class元素用于指定要FindBugs分析哪些JAR、类文件或者目录。
分析多个JAR或者类文件时,要为每一个文件指定一个单独的class元素。
除非加入了projectFile元素,否则需要class元素。
更多细节请参阅FindBugs手册。
第4行:
用嵌套元素auxClasspath列出应用程序的依赖性。
这些是应用程序需要但是不希望FindBugs分析的类。
如果没有列出应用程序的依赖关系,那么FindBugs仍然会尽可能地分析类,但是在找不到一个缺少的类时,它会抱怨。
与class元素一样,可以在FindBugs元素中指定多个auxClasspath元素。
auxClasspath元素是可选的。
第5行:
如果指定了sourcePath元素,那么path属性应当表明一个包含应用程序源代码的目录。
指定目录使FindBugs可以在GUI中查看XML结果时突出显示出错的源代码。
这个元素是可选的。
上面就是基本内容了。
让我们提前几个星期。
过滤器
您已经将FindBugs引入到了团队中,并运行它作为您的每小时/每晚编译过程的一部分。
当团队越来越熟悉这个工具时,出于某些原因,您决定所检测到的一些缺陷对于团队来说不重要。
也许您不关心一些类是否返回可能被恶意修改的对象——也许,像JEdit,有一个真正需要的(honest-to-goodness)、合法的理由调用System.gc()。
总是可以选择“关闭”特定的检测器。
在更细化的水平上,可以在指定的一组类甚至是方法中查找问题时,排除某些检测器。
FindBugs提供了这种细化的控制,可以排除或者包含过滤器。
当前只有用命令行或者Ant启动的FindBugs中支持排除和包含过滤器。
正如其名字所表明的,使用排除过滤器来排除对某些缺陷的报告。
较为少见但仍然有用的是,包含过滤器只能用于报告指定的缺陷。
过滤器是在一个XML文件中定义的。
可以在命令行中用一个排除或者包含开关、或者在Ant编译文件中用excludeFilter和includeFilter指定它们。
在下面的例子中,假定使用排除开关。
还要注意在下面的讨论中,我对“bugcode”、“bug”和“detector”的使用具有某种程度的互换性。
可以有不同的方式定义过滤器:
∙匹配一个类的过滤器。
可以用这些过滤器忽略在特定类中发现的所有问题。
∙匹配一个类中特定缺陷代码(bugcode)的过滤器。
可以用这些过滤器忽略在特定类中发现的一些缺陷。
∙匹配一组缺陷的过滤器。
可以用这些过滤器忽略所分析的所有类中的一组缺陷。
∙匹配所分析的一个类中的某些方法的过滤器。
可以用这些过滤器忽略在一个类中的一组方法中发现的所有缺陷。
∙匹配在所分析的一个类中的方法中发现的某些缺陷的过滤器。
可以用这些过滤器忽略在一组方法中发现的特定缺陷。
知道了这些就可以开始使用了。
有关其他定制FindBugs方法的更多信息,请参阅FindBugs文档。
知道如何设置编译文件以后,就让我们更详细地分析如何将FindBugs集成到编译过程中吧!
回页首
将FindBugs集成到编译过程中
在将FindBugs集成到编译过程当中可以有几种选择。
总是可以在命令行执行FindBugs,但是您很可能已经使用Ant进行编译,所以最自然的方法是使用FindBugsAnt任务。
因为我们在如何运行FindBugs一节中讨论了使用FindBugsAnt任务的基本内容,所以现在讨论应当将FindBugs加入到编译过程中的几个理由,并讨论几个可能遇到的问题。
为什么应该将FindBugs集成到编译过程中?
经常问到的第一个问题是为什么要将FindBugs加入到编译过程中?
虽然有大量理由,最明显的回答是要保证尽可能早地在进行编译时发现问题。
当团队扩大,并且不可避免地在项目中加入更多新开发人员时,FindBugs可以作为一个安全网,检测出已经识别的缺陷模式。
我想重申在一篇FindBugs论文中表述的一些观点。
如果让一定数量的开发人员共同工作,那么在代码中就会出现缺陷。
像FindBugs这样的工具当然不会找出所有的缺陷,但是它们会帮助找出其中的部分。
现在找出部分比客户在以后找到它们要好——特别是当将FindBugs结合到编译过程中的成本是如此低时。
一旦确定了加入哪些过滤器和类,运行FindBugs就没什么成本了,而带来的好处就是它会检测出新缺陷。
如果编写特定于应用程序的检测器,则这个好处可能更大。
生成有意义的结果
重要的是要认识到这种成本/效益分析只有在不生成大量误检时才有效。
换句话说,如果在每次编译时,不能简单地确定是否引入了新的缺陷,那么这个工具的价值就会被抵消。
分析越自动化越好。
如果修复缺陷意味着必须吃力地分析检测出的大量不相干的缺陷,那么您就不会经常使用它,或者至少不会很好地使用它。
确定不关心哪些问题并从编译中排除它们。
也可以挑出确实关注的一小部分检测器并只运行它们。
另一种选择是从个别的类中排除一组检测器,但是其他的类不排除。
FindBugs提供了使用过滤器的极大灵活性,这可帮助生成对团队有意义的结果,由此我们进入下一节。
确定用FindBugs的结果做什么
可能看来很显然,但是您想不到我参与的团队中有多少加入了类似FindBugs这样的工具而没有真正利用它。
让我们更深入地探讨这个问题——用结果做什么?
明确回答这个问题是困难的,因为这与团队的组织方式、如何处理代码所有权问题等有很大关系。
不过,下面是一些指导:
∙可以考虑将FindBugs结果加入到源代码管理(SCM)系统中。
一般的经验做法是不将编译工件(artifact)放到SCM系统中。
不过,在这种特定情况下,打破这个规则可能是正确的,因为它使您可以监视代码质量随时间的变化。
∙可以选择将XML结果转换为可以发送到团队的网站上的HTML报告。
转换可以用XSL样式表或者脚本实现。
有关例子请查看FindBugs网站或者邮件列表(请参阅参考资料)。
∙像FindBugs这样的工具通常会成为用于敲打团队或者个人的政治武器。
尽量抵制这种做法或者不让它发生——记住,它只是一个工具,它可以帮助改进代码的质量。
有了这种思想,在下一部分中,我将展示如何编写自定义缺陷检测器。
回页首
结束语
我鼓励读者对自己的代码试用静态分析工具,不管是FindBugs、PMD还是其他的。
它们是有用的工具,可以找出真正的问题,而FindBugs是在消除误检方面做得最好的工具。
此外,它的可插入结构提供了编写有价值的、特定于应用程序的检测器的、有意思的测试框架。
在本系列的第2部分中,我将展示如何编写自定义检测器以找出特定于应用程序的问题。
FindBugs,第2部分:
编写自定义检测器
FindBugs,第2部分:
编写自定义检测器
FindBugs,第2部分:
编写自定义检测器
如何编写自定义检测器以查找特定于应用程序的问题
FindBugs是一种可以扩展和定制以满足自己团队独特要求的静态分析工具。
在本系列的第2部分中,高级软件工程师ChrisGrindstaff向您展示如何创建特定于应用程序的缺陷检测器。
1
评论:
ChrisGrindstaff(chris@gstaff.org),软件工程师
关闭[x]
ChrisGrindstaff是在北加利福尼亚ResearchTrianglePark工作的IBM高级软件工程师。
Chris在7岁时编写了他的第一个程序,当时他让小学老师认识到“键入”句子与手写它们一样费力。
Chris目前参与了不同的开放源代码项目。
他大量使用Eclipse并编写了几个流行的Eclipse插件程序,可以在他的网站找到这些插件程序。
可以通过cgrinds@或者chris@gstaff.org与Chrise联系。
2004年6月01日
∙
内容
o编写自定义缺陷检测器
o结束语
o参考资料
o评论
在本系统的第一篇文章中,我展示了如何设置和执行FindBugs。
现在我们将分析FindBugs最强大的功能——自定义检测器。
首先,我将说明为什么自定义缺陷检测器很有用,然后我将引导读者完成一个详细的例子。
编写自定义缺陷检测器
为什么要编写自定义缺陷检测器?
我在被要求对一个小组的性能问题进行检查时遇到了这个问题。
很显然小组自已开发的日志框架(像所有日志框架一样)随着时间而增大。
原来是随意地大量调用Logger。
不幸的是,随着小组的扩大,他们的应用程序的性能也在变差,因为他们总是产生昂贵的日志消息——而当日志框架发现禁用日志后,这些消息只能是被它抛弃。
解决这个问题的标准方式是在构造昂贵的日志消息之前,首先检查是否启用了日志。
换句话说,使用一个像清单1这样的监护子句:
清单1.监护日志示例
if(Logger.isLogging()){
Logger.log("perf",anObjectWithExpensiveToString+anotherExpensiveToString);
}
本系列的第一篇文章“改进代码质量”介绍了FindBugs,这是一个检查类或者JAR文件以发现可能存在的问题的静态分析工具,并展示了如何有效地使用它。
这个小组认定这是一种恰当的日志做法,并对现有的代码加以改变以体现新的做法。
在这个非常大的项目的截止时间快到时听到还有很多地方未改完是不会让人感到意外的。
小组需要有更好的方法找出尚未修改的地方。
因为本文讨论的是FindBugs,所以我们将用FindBugs解决这个问题。
目标是编写一个FindBugs检测器,它将找出代码中调用日志框架而未被包装在监护子句中的所有地方。
当初编写这个检测器时,我将问题分解为几个单独的步骤:
1.首先用一条未监护的日志语句编写一个测试案例。
2.其次,查看FindBugs源代码以查找类似于我要编写的检测器类似的检测器。
3.然后创建正确打包的JAR文件(使用编译脚本),使FindBugs知道如何装载未监护检测器。
4.运行这个测试案例并实现代码使测试通过。
5.最后,加入更多测试案例,继续这个过程直到最后完成。
在浏览代码时,我特别检查了BytecodeScanningDetector和ByteCodePatternDetector的子类型。
实现扫描检测器要做更多工作,但是它们能检测更一般类型的问题。
如果所要检测的问题可以表述为一组字节码模式,则模式检测器是一种好的选择。
它的一个好例子是BCPMethodReturnCheck检测器——它查找那些不同方法的返回类型有可能被忽略的地方。
BCPMethodReturnCheck可以很容易地描述为一组模式,它查找在POP或者POP2指令后面调用某些方法的地方。
绝大多数检测器目前被编写为扫描检测器,尽管我认为这仅仅是因为开发人员还没有足够的时间转移到ByteCodePatternDetector。
我决定使用FindRunInvocations作为例子,主要是因为它是最小的一种检测器。
对我来说如何实现使用一组模式的检测器还不是很明确。
FindBugs利用了ByteCodeEngineeringLibrary,或称为BCEL(请参阅参考资料),以实现其检测器。
所有字节码扫描检测器都基于visitor模式,FindBugs实现了这个模式。
它提供了这些方法的默认实现,在实现自定义检测器时要覆盖这些方法。
请分析BetterVisitor及其子类以获得更多细节