提高代码的质量,除了要提高逻辑上的控制以及业务流程的理解外,代码本身也存在提高的空间,例如一些潜在的问题可以很早的就避免。类似于编码规范上的内容,如果全靠编码人员进行自行检查,那么无疑需要很大的工作量,如果可以使用代码的静态检查工具进行检查的话,那么将大大的提高编码的效率。
本文是提高代码质量系列文章的第二篇,主要介绍了如何使用checkstyle工具进行代码的自动化检查,以规避一些潜在的问题并找出代码的逻辑错误。
1. 什么是checkstyle?
Checkstyle是一个用于帮助编写Java代码的程序员来遵守特定的编码标准。Checkstyle可以自动进行代码的检查,因为代码检查非常重要,但是又很枯燥,所以可以自动的进行代码检查无疑节省了很多的工作量。Checkstyle尤其适用于强制使用编码标准的项目。
Checkstyle是完全可以进行自定义配置的,并且可以支持几乎任何的编码标准。例如提供了支持Sun Code Conventions编码标准的样例配置文件以及一些其他的样例配置文件。
Checkstyle可以检查源代码的很多方面,checkstyle的本意是检查代码的布局及样式,但是从版本3之后,就已经追加了更多的检查项目。现在的checkstyle版本已经可以检查诸如设计问题,重复代码,以及double checked locking等bug。具体请参考标准检查项和可选检查项。
可以在SourceForge的下载页面下载Checkstyle的最新版本。
相关工具:如果将Checkstyle集成到构建过程中的话,那么将起到最大的作用。Checkstyle的发布版本包含以下内容:
→ Ant Task
→ 命令行工具
另外,也有很多第三方的插件可供使用,例如Eclipse、WSAD、IntelliJ IDEA、NetBeans等流程的Java开发IDE。
2. CheckStyle的配置
Checkstyle的配置用来指定哪些模块适用于Java源文件。模块是一个以Checker模块为根元素的树结构。modules的下一层元素包括如下的内容:
® FileSetChecks:一些以输入文件为参数的模块,并引发错误消息。
® Filters:过滤审核事件的模块,包括错误消息。
® AuditListeners:报告被接受的事件。
很多检查是TreeWalker FileSetCheck模块的子模块。TreeWalker通过逐个的将Java源文件转换成抽象的语法树,然后循环它的每个子模块来处理检查结果。每个子模块检查各自特定的方面。
Checkstyle从XML文件获取配置,在XML文档中指定了配置的模型的层次以及他们的属性。当使用命令行执行Checkstyle命令(或者在ant任务中使用Checkstyle的时候)是需要提供一个包含配置文档的文件。Checkstyle的发布包中的doc目录中包含了一个样例的配置文件sun_checks.xml可以检查Sun的编码规范。
Modules
XML配置文档中由元素的name属性指定的module元素。下面是一个典型的配置片段:
<module name="Checker">
<module name="PackageHtml"/>
<module name="TreeWalker">
<module name="AvoidStarImport"/>
<module name="ConstantName"/>
<module name="EmptyBlock"/>
</module>
</module>
在上面的配置中:
® 根模块Checker包含了子模块FileSetChecks PackageHtml和TreeWalker。(PackageHtml模块检查所有的包都要有文档)
® 模块TreeWalker包含了子模块AvoidStarImport、ConstantName和EmptyBlock。这些子模块分别检查Java源文件是否有星号引入语句、合法的常两名以及是否包含空块。
对于每个配置模块,checkstyle通过module元素的name属性指定要加载的类。加载模块的类的规则如下:
® 根据指定的包名,例如加载类com.puppycrawl.tools.checkstyle.TreeWalker,<module name =” com.puppycrawl.tools.checkstyle.TreeWalker”>,这有助于第三方的模块集成到配置中。
® checkstyle预定义的包名,例如com.puppycrawl.tools.checkstyle.checks和com.puppycrawl.tools.checkstyle.filters以及它的子包(仅包含在Checkstyle发布版本中)
® 上述规则中与Check连接而成的,例如com.pupycrawl.tools.checkstyle.checks.ConstantNameCheck类的话,可以使用ConstantName作为module的名字
属性
属性用于控制模块如何执行任务的。每个模块的属性都有一个默认值。下面是三个例子用于指定属性:
<module name="MethodLength">
<property name="max" value="60"/>
</module>
max的默认值为150,可以将其自定义为60,如果默认值可以满足检查的要求,那么就不要修改默认值。
<module name="Checker">
<module name="PackageHtml"/>
<module name="TreeWalker">
<property name="tabWidth" value="4"/>
<module name="AvoidStarImport"/>
<module name="ConstantName"/>
...
</module>
</module>
模块的属性是可以继承的,例如上面例子中的TreeWalker的属性tabWidth,就可以被AvoidStarImport和ConstantName等子模块继承。
<property name="headerFile" value="${checkstyle.header.file}"/>
属性是可以被扩展的,可以在运行时指定属性的值。
<module name="JavaDocMethod">
<property name="severity"
value="${checkstyle.javadoc.severity}"
default="error"/>
</module>
可以在指定属性值后设置一个默认值,当属性值没有被设置时,使用默认的属性值,如上面的例子中checkstyle.javadoc.severity没有被设置时,severity属性的值就是error。
Checker
所有的配置都有一个根模块Checker,Checker包含以下内容:
® FileSetCheck:用于检查文件的模块
® Filter:用于过滤审核事件
® AuditListener:报告被过滤的事件
Checker还定义被所有其他模块继承的属性:
® basedir:基准目录名,字符串类型,默认值为null
® localeCountry:区域设置,字符串类型,可以是空字符串或者大写的ISO 3166标准的2个大写代码,默认值为JVM的区域设置
® localeLanguage:消息的区域语言,字符串类型,可以是空字符串或者是小写的ISO 639代码,默认值是JVM的区域语言
TreeWalker
FilterSetCheck TreeWalker 检查单个的Java源文件,并定义适用于检查这些文件的属性:
® cacheFile:缓存文件,用于缓存已经通过检查的文件,避免重复检查文件。字符串类型,默认为null
® tabWidth:tab字符的空格数。整型,默认为8
® fileExtensions:用于标识java文件的扩展名。字符串类型集合,默认为java
® charset:文件的编码格式。字符串类型,默认为系统属性file.encoding
<module name="Checker">
<module name="TreeWalker">
<property name="cacheFile" value="target/cachefile"/>
<property name="tabWidth" value="4"/>
<property name="charset" value="UTF-8"/>
...
</module>
</module>
可以在TreeWalker中写重复的模块来应用不同的检查。
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF"/>
</module>
<module name="MethodLength">
<property name="tokens" value="CTOR_DEF"/>
<property name="max" value="60"/>
</module>
Severity
每个检查都有一个severity属性,这是Checkstyle审查分配给检查的所有违反项。默认的severity等级是error。
使用下面的定义就可以输出转换警告:
<module name="Translation">
<property name="severity" value="warning"/>
</module>
Filters
checkstyle模块可以包含一系列的Filter子模块用于过滤审查事件,包括checks产生的错误消息。过滤器可以接受获取拒绝审查事件。如果所有的过滤器都接受审查事件,那么checker就报告该事件。
SeverityMatchFilter
这个过滤器根据severity等级来决定审查事件。这个过滤器包含如下的属性:
® severity:过滤器的severity的等级。severity类型,默认值为error。
® acceptOnMatch:如果该属性的值为true,那么当且仅当事件的severity等级和属性severity的值相匹配时,才接受审查。如果值为false的话,那么当且仅当事件的severity等级与severity属性值不匹配时才接受审查。boolean型,默认值为true。
<module name="SeverityMatchFilter">
<property name="severity" value="info"/>
<property name="acceptOnMatch" value="false"/>
</module>
一些其他的过滤器请参考checkstyles文档。
3. 运行Checkstyle
可以通过两种方式来运行Checkstyle,一种是使用Ant Task,即在Ant构建脚本中执行Checkstyle,另一种就是命令行。当然还有很多IDE的集成插件,在后面进行介绍。
checkstyle的ant task用于在指定的Java文件中运行Checkstyle。checkstyle ant task在Ant 1.5版本下测试完成。最新版本的Checkstyle中包含了ant task。
最简单的安装方式就是将checkstyle-all-4.4.jar加入到classpath中。这个jar文件中包含了所有运行Checkstyle所需的类。如果不这样做的话,下面的这些类必须添加到classpath中:
® checkstye-4.4.jar
® ANTLR 2.7.2中的类,在发布版本中已经包含了这个jar文件
® Jakarta Commons中的Beanutils类、Collections类、Logging类,这些jar包都包含在发布版本中
® 符合JAXP规范的XML解析器,在JDK 1.4版本以后就已经包含了
如果要使用checkstyle ant task,需要使用taskdef定义checkstyle任务:
<taskdef resource="checkstyletask.properties"
classpath="/path/to/checkstyle-all-4.4.jar"/>
或者假设Checkstyle位于全局的classpath中,可以使用如下的taskdef定义:
<taskdef resource="checkstyletask.properties"/>
checkstyle任务的参数:
file:需要检查的文件。
config:指定配置文件。
configURL:指定配置文件的URL。
(config和configURL中必须指定一个)
properties:指定包含扩展属性值的属性文件。
packageNamesFile:指定一个包含配置的package名的文件。
failOnViolation:如果检查出错误时,是否停止。默认为true。
failureProperty:如果发生错误时,需要设置的属性的名称。
maxErrors:允许发生错误ude最大数。默认为0。
maxWarnings:允许发生的警告的最大数。默认值为整型的最大值。
classpath:查询类时使用的类路径。默认为当前classpath。
classpathref:类路径的引用。
嵌套元素:checkstyle任务支持以下的嵌套元素:<fileset>、<classpath>、<formatter>和<property>。其中<formatter>元素的参数如下:
type:生成的输出内容的类型,合法的值包括plain和xml。默认的值为plain。
toFile:将输出写入的文件。默认为标准输出。
useFile:是否将输出写入到文件,默认为true。
<property>元素指定提供扩展属性的值的属性文件。该元素的参数如下:
key:属性的键值。
value:字符串格式的属性的值。
file:文件格式的属性的值。
(value和file参数必须设置一个)
下面是一些例子:
<checkstyle config="docs/sun_checks.xml" file="Check.java"/>
<checkstyle config="/path/to/site/sun_checks.xml">
<fileset dir="src/checkstyle" includes="**/*.java"/>
<!-- Location of cache-file. Something that is project specific -->
<property key="checkstyle.cache.file" file="target/cachefile"/>
</checkstyle>
<checkstyle config="docs/sun_checks.xml">
<fileset dir="src/checkstyle" includes="**/*.java"/>
<formatter type="plain"/>
<formatter type="xml" toFile="build/checkstyle_errors.xml"/>
</checkstyle>
<checkstyle config="docs/sun_checks.xml"
packageNamesFile="myPackageNames.xml"
file="Check.java"/>
<target name="checkstyle"
description="Generates a report of code convention violations.">
<checkstyle config="docs/sun_checks.xml"
failureProperty="checkstyle.failure"
failOnViolation="false">
<formatter type="xml" tofile="checkstyle_report.xml"/>
<fileset dir="src" includes="**/*.java"/>
</checkstyle>
<style in="checkstyle_report.xml" out="checkstyle_report.html" style="checkstyle.xsl"/>
</target>
<!-- run this target as part of automated build -->
<target name="checkstyle-nightly"
depends="checkstyle"
if="checkstyle.failure"
description="Sends email if checkstyle detected code conventions violations.">
<!-- use your own server and email addresses below. See Ant documentation for details -->
<mail from="[email protected]"
tolist="[email protected],[email protected]"
mailhost="mailbox.some.domain"
subject="Checkstyle violation(s) in project ${ant.project.name}"
files="checkstyle_report.html"/>
</target>
Checkstyle的命令行使用方式:
java -D<property>=<value> /
com.puppycrawl.tools.checkstyle.Main /
-c <configurationFile> [-n <packageNameFile>] /
[-f <format>] [-p <propertiesFile>] [-o <file>] /
[-r <dir>] file...
* -n packageNamesFile - 指定使用的package name文件。
* -f format - 指定输出的格式。
* -p propertiesFile - 指定使用的属性文件。
* -o file - 指定输出文件。
* -r dir - 指定需要遍历java文件的目录。
4. 在Eclipse中使用Checkstyle
Checkstyle的Eclipse插件的官方地址为:http://eclipse-cs.sourceforge.net/。安装Checkstyle插件的过程与安装PMD、Findbugs的过程一致,这里就不做介绍了。
下面是安装Eclipse插件的过程与注意点(注意点适用于所有的Eclipse Plug-in)
The first way to install the Eclipse Checkstyle Plug-in is via the update site.
1. Within Eclipse go to Help->Software Updates->Find and Install
2. Choose Search for new features to install and press Next
3. Create a New Remote Site...
4. Input a name to your liking (for instance Checkstyle Plug-in) and input the following URL: http://eclipse-cs.sourceforge.net/update
5. Click your way through the following pages to install the plug-in.
Alternatively the Checkstyle Eclipse Plug-in can be downloaded from the Eclipse Checkstyle Plug-in's SourceForge project page. The download package consists of a single zip file that contains both the Checkstyle Plug-in and the Checkstyle core engine.
Once you have downloaded the distribution zip file extract its contents. First, exit from Eclipse. Next, copy the com.atlassw.tools.eclipse.checkstyle_<version> folder to your Eclipse plug-in folder and restart Eclipse. That's all there is to it, you are now ready to run Checkstyle.
Important Note: If you did not use the update site to install the plug-in please make sure to restart Eclipse with the -clean option at least once. This is because Eclipse caches a lot of information about plug-ins. In case you just updated the plug-in from a previous version this information cached by Eclipse is not up to date - resulting in the plug-in failing with ClassNotFoundExceptions and other serious issues.
Important Note: As of plug-in release 4.0.0 only Eclipse 3.0 and newer is supported. Eclipse 2.x.x support is discontinued. Please use older plug-in versions with Eclipse 2.x.x. - downloadable from the SourceForge Project Page
关于在Eclipse中使用Checkstyle的方法,请参考Eclipse-checkstyle官方文档:
http://eclipse-cs.sourceforge.net/getting_started.html
以上,笔者只是简单的介绍了checkstyle的一些基本内容,当然,更多的知识需要去学习与掌握。只有先进行一定的了解,例如,必须先知道要进行代码的静态检查,提高代码的质量,然后考虑代码的人工检查还是自动检查,再考虑使用自动检查工具,最后去熟悉流行的业界检查工具标准,最后学习如何使用检查工具。
希望笔者的抛砖引玉的文章可以给各位读者带来一些帮助。