服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

用pmd自动执行java代码静态分析

 

 一、基础知识
  pmd是一种分析java代码错误的工具。与其他分析工具不同的是,pmd通过静态分析获知代码错误。也就是说,在不运行java程序的情况下报告错误。pmd附带了许多可以直接使用的规则,利用这些规则可以找出java源程序的许多问题,例如没有用到的变量、多余的变量创建操作、空的catch块,等等。此外,用户还可以自己定义规则,检查java代码是否符合某些特定的编码规范。例如,你可以编写一个规则,要求pmd找出所有创建thread和socket对象的操作。
  最初,pmd是为了支持cougaar项目而开发的。cougaar是美国国防高级研究计划局(defense advanced research projects agency,darpa)的一个项目。darpa开放了pmd的源代码,所以pmd被发布到了sourceforge网站上。不久前,pmd的下载次数就超过了14000次,页面浏览次数超过了130000次。更重要的是,在源代码开放作者的努力下,越来越多的pmd规则和ide插件被开发出来,然后加入到了pmd的核心项目之中。
  你可以从pmd的网站下载pmd的二进制版本,或下载带源代码的版本,下载得到的都是zip文件。假设你下载了二进制版本,先把它解压缩到任意一个目录。接下来怎么做,就要看你准备怎么用它――最简单的,如果要在一个java源代码目录中运行pmd,只需直接在命令行上运行下面的命令:

c:/data/pmd/pmd>java -jar lib/pmd-1.02.jar c:/j2sdk1.4.1_01/src/java/util
text rulesets/unusedcode.xml

  输出结果类如:

c:/j2sdk1.4.1_01/src/java/util/abstractmap.java 650
avoid unused local variables such as 'v'
c:/j2sdk1.4.1_01/src/java/util/date.java 438
avoid unused local variables such as 'millis'

  除了直接在命令行上运行pmd之外,还可以通过ant、maven或者各种集成开发环境(ide)运行pmd,例如jedit、netbeans、eclipse、emacs、ideaj和jbuilder等。

  二、内建规则
  pmd本身就附带了许多规则。下面是几个例子。

没有用到的代码显然是应该被清除的。
public class foo {
// 下面这个实例变量没有用到
private list bar = new arraylist(500);
}
如果用一个接口也能达到同样的目标,为什么要返回一个具体的类?例如,下例可以改用list接口。
public arraylist getlist() {
return new arraylist();
}
当if的条件为真时,if代码块其实不做任何事情。下面这段代码其实可以写得更加简洁一些。
public void dosomething(int y) {
if (y >= 2) {
} else {
system.out.println("less than two");
}
}
为什么要创建一个新的string对象?只要改用string x="x"就可以了。
string x = new string("x");



  pmd还包含其他许多内建规则,但从上面几个例子已经可以看出pmd的基本工作方式。只要定义适当的静态规则,pmd就可以象一个富有经验的程序员那样,帮你指出代码存在的问题。

  三、工作原理

  pmd的核心是javacc解析器生成器。pmd结合运用javacc和ebnf(扩展巴科斯-诺尔范式,extended backus-naur formal)语法,再加上jjtree,把java源代码解析成抽象语法树(ast,abstract syntax tree)。显然,这句话不那么好懂,且看下文具体说明。

  从根本上看,java源代码只是一些普通的文本。不过,为了让解析器承认这些普通的文本是合法的java代码,它们必须符合某种特定的结构要求。这种结构可以用一种称为ebnf的句法元语言表示,通常称为“语法”(grammar)。javacc根据语法要求生成解析器,这个解析器就可以用于解析用java编程语言编写的程序。

  不过实际运行中的pmd还要经过jjtree的一次转换。jjtree是一个javacc的插件,通过ast扩充javacc生成的解析器。ast是一个java符号流之上的语义层。有了jjtree,语法分析的结果不再是“system, ., out, ., . println”之类的符号序列,而是一个由对象构成的树型层次结构。例如,下面是一段简单的java代码以及与之对应的ast。

java源代码:
public class foo {
public void bar() {
system.out.println("hello world");
}
}
对应的抽象语法树
compilationunit
typedeclaration
classdeclaration
unmodifiedclassdeclaration
classbody
classbodydeclaration
methoddeclaration
resulttype
methoddeclarator
formalparameters
block
blockstatement
statement
statementexpression
primaryexpression
primaryprefix
name
primarysuffix
arguments
argumentlist
expression
primaryexpression
primaryprefix
literal



  四、编写规则

  前面我们看到了java源代码以及与之对应的对象层次结构。下面我们就要利用这些对象编写pmd规则检查代码存在的问题。

  一般地,一个pmd规则可以看成一个visitor,它遍历ast,寻找多个对象之间的一种特定模式,这种模式表示代码存在的问题。问题模式可能简单也可能复杂,简单的如查找代码中是否包含new thread关键词,复杂的如确定一个类是否正确覆盖了equals和hashcode。

  下面是一个寻找空if语句的简单pmd规则。

//扩展abstractrule,以启用visitor模式
public class emptyifstmtrule extends abstractrule implements rule {
//当源代码中出现一个block,下面的方法被调用
public object visit(astblock node, object data){
//如果父节点是一个if语句且代码块里面没有任何内容
if ((node.jjtgetparent().jjtgetparent() instanceof astifstatement)
&& node.jjtgetnumchildren()==0) {
//肯定代码存在问题。把一个ruleviolation加入到report。
rulecontext ctx = (rulecontext)data;
ctx.getreport().addruleviolation(createruleviolation(ctx,
node.getbeginline()));
}
//继续检查树的下一个节点
return super.visit(node, data);
}
}

  也许你不能一下子掌握这段代码,其实它的思路还是比较简单的:
  #扩展abstractrule基类。
  #声明一个“钩子”,一旦我们感兴趣的节点出现,它就会被调用(称为“回调”)。在上面的例子中,我们要求在每一个astblock出现时得到通知,所以声明visit(astblock node, object data)。
  #在回调函数中,判断是否出现了我们正在检查的问题。本例我们检查是否存在空的if块,所以先判断当前是否在astifstatement之内,然后判断它是否有子节点。
  当然,我们还可以按照另一种方法进行检查:声明一个要求检查astifstatement的回调函数,然后在回调函数中检查是否存在子节点。
  五、配置规则
  写好自定义规则之后,接下来要把它加入到某个pmd规则集。所谓pmd规则集,就是由一组pmd规则构成的集合。每个pmd规则集由一个xml文件定义,下面是一个pmd规则的配置信息的例子:
<rule name="emptyifstmt"
message="避免使用空的if语句"
class="net.sourceforge.pmd.rules.emptyifstmtrule">
<description>
找到空的if语句:if检查了条件,但if块里面没有任何内容。
</description>
<priority>3</priority>
<example>
<![cdata[
if (absvalue < 1) {
// not good
}
</xmlcdata>
</example>
</rule>
  可以看出,规则配置文件包含了许多有用的信息。要运行新添加的规则,只需把规则集xml文件和java源代码文件放入classpath,然后运行pmd。
  结束语:本文介绍了pmd如何在不编译代码的情况下分析和寻找代码存在的问题,通过几个简单的例子了解了ebnf语法、javacc和ast,以及如何用pmd检查代码存在的问题、如何编写和运用定制pmd规则等。愿pmd能够助你一臂之力!

扫描关注微信公众号