PMD (别向报信者开火)

在这里插入图片描述

PMD 工具简介

PMD 是一款非常实用的 跨语言静态代码分析工具 (Cross-language Static Code Analyzer),它能运行于不同平台且实用简单。PMD 与其他代码分析工具,如 CheckStyle,SonarQube,最大的不同在于 PMD 不用通过编译过程(即 静态),这样能让分析者聚焦到代码本身的特点,而不用担心代码的编译问题。

PMD 的最新版本是 6.26.0,相关使用文档见 [https://pmd.github.io/pmd-6.26.0/] 。本文主要简单总结了一下使用 PMD 工具来分析 Java 代码的基本情况。

这里的 PMD 并不是 项目管理文档 (Project Management Documentation) 的缩写,而是 一款代码质量的分析工具。至于 PMD 的全程到底是什么,我也没有找到相关材料,可能是作者有感而发想出来的吧 😉

在 Windows 上安装 PMD

安装 PMD 非常简单,在其 github 网页的发行页面下载最新版本 [pmd-bin-6.26.0.zip],解压后将其 bin 目录添加到 Windows 执行路径 Path 中。例如,我将 PMD 解压放在了 F:/Softwares/ 目录下,那么我需要将 F:/Softwares/PMD/bin 路径添加到 环境变量的 Path 中去。红色框框中 .bat 文件就是 Windows 平台中执行的命令。
在这里插入图片描述

设置到环境变量并非必须,如果你能忍受每次运行 pmd 工具时都打开一次 F:/Softwares/PMD/bin 目录并且点击一次 pmd.bat 的话,也完全不必要设置相关的环境变量 😃

设置好环境变量后,我们可以在命令行中检验一下安装是否成功,输入命令 -h 即可。如果出现了 pmd 的参数列表的解释,就说明安装 pmd 成功。

在这里插入图片描述

PMD 的命令行参数

PMD 工具主要的运行方式是 命令行方式,即 pmd +[Options] 模式。下面展示了几个常用的参数 Options 及其含义,具体的参数介绍请见 [PMD Options]

  • -d,确定要分析代码的位置 (必须)。该参数可以是文件目录(分析该目录中的所有 java 文件)也可以是具体的 java 文件。 用法:pmd -d F:/workspace/proj1/src/A.java
  • -R,确定代码分析的规则集合 (必须)。这里的 “规则” 即分析代码的哪些内容,如是否包含上帝类,是否存在变量没被引用等,这些规则通常保存在 xml 文件中。 用法:pmd -R F:/rulesets/rules.xml
  • -f,确定分析结果的输出格式。默认是 txt 文本文件,其他支持的输出格式包括 html,xml,csv,json,xslt 等。 用法:pmd -f txt
  • -debug,展示运行 PMD 时的日志信息。这个参数要慎点,因为它会输出很大的日志信息,一般用于 PMD 出错时的调试中。用法:pmd -debug
  • -help,展示 PMD 的参数列表信息。用法: pmd -help
设置分析规则

在设置 -R 参数时,我们需要定义规则集合,即我们要分析哪些内容。PMD 有自己一套写好的规则集供我们调用,即 Best Practices,Code Style,Design,Documentation,Error Prone,Multithreading,Performance,Security,Additional rulesets,这 9 个规则集中存有大量的常见的预定义的规则,这些规则基本上涵盖了代码分析的方方面面,详情请参见 [Java Rules]

调用规则集的方法为:Step 1. 创建空白 xml 文件,Step 2. 写入 Ruleset 的基本信息,Step 3. 写入 Rule 规则。下图展示了一个基本的规则集文件(命名为 ruleset.xml)的基本结构,

<?xml version="1.0"?>
<ruleset name="myruleset"
		xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 
		                    https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
    <description>My first ruleset</description>
    
    <!-- Rules here ... -->
    <rule ref="category/java/bestpractices.xml/UnusedLocalVariable" />
	
</ruleset>

因此规则集文件的核心在于 <rule> 部分的书写。例如,我们要检查是否有未被使用的局部变量,可直接在 <rule> 中规定要引用的规则。通过查找其文档,我们可以知道 [UnusedLocalVariable] 的规则解释,

在这里插入图片描述

因此在实际书写 ruleset.xml 过程中,我们只需要更改属性值 ref 即可。大部分规则的评判结果是 “是” 或 “否”,还有一部分规则的评判结果是一个 “阈值”,若某个规则评判结果超过了这个 “阈值” PMD 就会上报。

例如,规则 [CyclomaticComplexity] 是判断书写的 Java 代码的圈复杂度是否过高。默认情况下,当类的复杂度高于 80 或函数的复杂度高于 10,PMD 就会上报这个信息。在实际应用中,我们可以手动对这个阈值进行修改。我们将 80 改为 100,将 10 改为 20。具体的规则可以写为,
在这里插入图片描述
上述 <rule> 中的 <properties> 中规定的就是属性要求,我们可以根据实际需要进行更改。

在实际应用中还有一种情况,就是我们现需要分析 Design 规则集 (即 category/java/design.xml) 中除 CyclomaticComplexity 和 TooManyMethods 这 2 条规则以外的其他规则时,我们可以写成如下形式,
在这里插入图片描述

简单实例展示

假设要分析的代码地址为 E:\workspace\CoreJavaPart1\src\chapter3\DataType.java ,分析规则文件地址为 F:\Softwares\PMD\ruleset\testrules.xml,要分析的代码为,

package chapter3;

/**
 * This class discusses 8 widely-used data-types in Java
 * @version 1.01 2020-8-11
 * @author yongfeng
 *
 */
public class DataType {	
	public static final double CM_PER_INCH = 2.54;
	public static void main(String[] args) {		
		System.out.println("There are 8 basic data types used in Java.\n"
				+ "They are: int, short, long, byte, float, double, char, boolean.");	
		// 4 types of integers
		int iAge = 28;
		short sNum1 = 32767;
		long lDateTime = 123456789l;
		byte bNum2 = 127;		
		// 2 types of floating numbers
		float fNum3 = 3.14159f;
		double dNum4 = 314.15926575d;	
		// a type of character
		char cChar1 = 'A';	
		// a type of boolean
		boolean fGate = true;
	}
}

沿用的规则为,

<rule ref="category/java/bestpractices.xml/UnusedLocalVariable" />

我们输入命令来分析 DataType.java 中未被使用的局部变量,

> pmd -d E:\workspace\CoreJavaPart1\src\chapter3\DataType.java -f text -R F:\Softwares\PMD\ruleset\testrules.xml

根据结果可知,PMD 能够准确找到 iAge,SNum1,lDateTime 等若干个未被使用的局部变量,这些变量被赋值但却没有被使用,说明此代码质量的确有待提升。

在这里插入图片描述

PMD 的其他功能

- 如何避开 PMD 的检查: 之前所讲的都是如何应用规则 <rule> 来检查代码质量,这十分常见。设想一下,某天你写了一段十分糟糕的代码,却又不想让 PMD 检查这段代码。一个折中的方法就是在这段代码(类,函数)前面加上一个注解。如下所示,PMD 在分析代码时会忽略对 Bar 类的检测。

// This will suppress all the PMD warnings in this class
@SuppressWarnings("PMD")
public class Bar {
    void bar() {
        int foo;
    }
}

如果你仅仅不想检查 Bar 类的某些规则,如 UnusedLocalVariable 和 UnusedPrivateMethod,代码为,

// This will suppress all the PMD warnings in this class
@SuppressWarnings({"PMD.UnusedLocalVariable", "PMD.UnusedPrivateMethod"})
public class Bar {
    void bar() {
        int foo;
    }
}

- 检测重复代码: 在单个文件或多个文件中检测是否有重复代码是一个非常不错的功能,PMD 本身并没有提供这个功能,但是我们可以使用与 PMD 一同下载下来的 CPD 工具来检测重复代码。

例如,我们想要查找 E:\workspace\CoreJavaPart1\src\chapter3\A.java 文件中重复代码的情况,A.java 的内容如下,很明显 A 类和 B 类中都有一段重复的 showVal() 方法。

package chapter3;  // Line 1

public class A {  
	private int val1; 
	public A(int a){  // Line 5
		val1 = a; 
	}  // Line 7
	public void showVal(){
		System.out.println(this.getClass().getName() + " -> value" + val1);
	}
}

class B{
	private int val1;
	public B(int b){
		val1 = b;
	}  // Line 17
	public void showVal(){
		System.out.println(this.getClass().getName() + " -> value" + val1);
	}
}

我们执行如下命令来检测 A.java 中的重读代码。其中 -minimum-tokens 规定了重复代码的字符数,-files 规定了检测重复代码的文件(可多个),CPD 完整参数请见 [CPD 参数]

> cpd --minimum-tokens 15 --files E:\workspace\CoreJavaPart1\src\chapter3\A.java --language java

执行结果如下所示,CPD 准确的找到了 2 段重复代码及其位置等信息!

在这里插入图片描述
- 创建自己的分析规则: PMD 本身并没有提供自定义分析规则功能,但是我们可以使用与 PMD 一同下载下来的 PMD Rule Designer 可视化工具来检测重复代码。直接运行主目录下的 designer.bat 文件即可。详细的文档参见 [Your first rule XPath]

这个 PMD Rule Designer 主要分为了 代码区,AST 展示区,和 XPath 区(即手写 rule 规则),个人感觉不是很好用,其中的 XPath 语法比较繁琐,个人不推荐使用这个工具。

在这里插入图片描述
- Java API: PMD 是可以与我们的 java 代码无缝衔接。换句话说,在命令行中能做的事,也可以写进 java 代码里。首先我们下载 PMD 的 jar 包,然后在代码中引入并传给其对应参数。

import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
...
public static void main(String[] args) throws IOException{
	// 方法一:将参数写成数组形式 传给 PMD 的 main 方法中
	String[] pmdArgs = {
			"-d", "E:/workspace/CoreJavaPart1/src/chapter3/DataType.java",
			"-R", "rulesets/java/quickstart.xml",
			"-f", "text"
	};
	PMD.main(pmdArgs);
		
	// 方法二:使用 PMDConfiguration 来配置对应的参数,再调用 PMD 的 doPMD 方法
	PMDConfiguration config = new PMDConfiguration();
	config.setInputFilePath("E:/workspace/CoreJavaPart1/src/chapter3/DataType.java");
	config.setRuleSets("rulesets/java/quickstart.xml");
	config.setReportFormat("xml");
	PMD.doPMD(config);
}

- Eclipse 插件: 使用 Eclipse 或者 IntellJ 的朋友们可以直接使用对应的插件,这里我以 Eclipse 为例,安装 PMD 的插件。由于 PMD 的版本只有 4.15 (目前最新 6.26),因此插件可能还有一点点小的 bug,不过不影响使用,我们在 Eclipse 中打开 Help -> Install New Software,并在弹出窗口中添加 pmd 插件地址,插件地址:https://dl.bintray.com/pmd/pmd-eclipse-plugin/updates/4.15/

在这里插入图片描述
在安装 PMD 插件成功后,我们可以查看右键菜单,如果其中出现了 PMD 选项则表示 PMD 插件已经成功安装。当我们要对某 Java 文件进行分析时,直接在待分析的文件上右键,选择 PMD 选项并点击 Check Code 选择项。之后 Eclipse 会弹出 Violations OverviewViolations Outline 这 2 个视图。

在这里插入图片描述
Violations Overview 视图会列出被分析的 Java 文件出现的问题(即违反的规则)总览。在这个视图中,Element 记录了待分析的文件,以及出现的被违背规则的种类。下图展现了 MethodArgumentCouldBe-Final,BeanMembersShouldSerialize 等 5 种被违背的规则。在此 Java 文件中,这 5 种规则被违背的次数记录在 # Violations 中,后续也计算出 平均千行代码违背规则的次数 (#Violations/KLOC),以及平均每个函数违背规则的次数 (#Violations/Method)。

在这里插入图片描述
Violations Outline 视图则详细地记录了每个被违背规则的位置(即代码行号,Line),违背规则的名称(Rule),以及相关的解释信息(Error Message)。例如,在源代码第 5 行处违背了一个 ShortVariable 的规则,同时 PMD 提示道:我们应当避免用如 a 这样的短名字来命名变量(ShortVariable:Avoid variables with short names like a)。

在这里插入图片描述
我们查看一下源代码,发现确实有一个变量被命名为 a,这确实是一个不好的编程习惯。依次类推,我们可以依次纠正后续的违背规则的地方,并消除 PMD 的报错信息。

总的来讲,PMD 是一款不错的 开源 静态代码分析工具,它能够 跨平台、跨语言 地对代码的质量进行分析(平台r如 Windows,Linux 等;语言如 Java,Scala 等)。由于其静态的特性,受到了很多科研人员的青睐,在 SonarQube,FindBugs,CheckStyle 等工具之后,人们又多了一个代码分析的出色工具。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值