关于java:PMD插件你必须掌握的代码质量工具

2次阅读

共计 7014 个字符,预计需要花费 18 分钟才能阅读完成。

当今的软件开发须要应用许多不同的工具和技术来确保代码品质和稳定性。PMD 是一个风行的动态代码剖析工具,能够帮忙开发者在编译代码之前发现潜在的问题。在本文中,咱们将探讨如何在 Gradle 中应用 PMD,并介绍一些最佳实际。

什么是 PMD?

PMD 是一个用于 Java 代码的动态代码剖析工具。它能够帮忙开发者找出潜在的问题,如代码反复、未应用的变量、谬误的异样解决等。PMD 反对多种规定,能够依据具体我的项目的须要进行配置。其工作原理参考 How PMD Works。

PMD 反对通过命令行界面(CLI, Command Line Interface for batch scripting)和其余多种集成形式,比方 Maven、Gradle、Java API 等等。

PMD 在 Gradle 中配置和应用

Gradle 中自带了 PMD 插件,插件的默认版本能够通过源码 DEFAULT_PMD_VERSION 晓得。应用和配置能够参考 The PMD Plugin,页面左上角能够抉择 Gradle 版本,确保查看的版本和你应用的 Gradle 版本统一,因为很多 PMD 的配置属性或者性能不肯定在每个版本都有。

通过页面左上角选了其余版本后跳转的地址是 Gradle 文档的首页,而不是 PMD 插件的文档页。咱们能够通过批改 The PMD Plugin 链接中的 8.0.2 为其余版本号即可跳转到对应 Gradle 版本蕴含的 PMD 插件文档的页面。比方:

以后最新版:https://docs.gradle.org/current/userguide/pmd_plugin.html

7.3.3 版本:https://docs.gradle.org/7.3.3/userguide/pmd_plugin.html

在我的项目 build.gradle 文件中减少以下内容利用插件和扩大 PMD,参考 Usage 和 Configuration,更多的配置属性能够参考 PmdExtension。

plugins {id 'pmd'}

pmd {
    // 是否将 PMD 后果输入到终端
    consoleOutput = true
    // 要应用的 PMD 版本
    toolVersion = "6.21.0"
    // 规定优先级阈值,低于这个优先级则会被疏忽
    rulesMinimumPriority = 5
    // 应用的规定集配置文件门路
    ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
}

插件会生成两个次要的 PMD TaskpmdMainpmdTest 别离对 main 和 test 两个我的项目源文件目录应用 PMD 进行代码查看。

找到 IDEA Gradle 窗口 > Tasks > other,双击生成的 Task;或者在我的项目根目录运行./gradlew pmdMain 都能够运行 PMD。查看后果将输入到终端中(前提是配置了consoleOutput = true),违反了 PMD 规定的类会给出残缺的跳转门路以及规定提示信息。

最初还会给出一个报告的地址,内容蕴含了输入到终端的信息。Problem 列出了规定的提醒,点击能够跳转到 PMD 规定形容文档对应的地位。

Gradle PMD Plugin 扩大属性

在这里咱们将 PMD 插件的扩大属性作用进行阐明,参考 PmdExtension,这个文档具体阐明了各个属性的作用、默认值和配置示例。如果文档形容的不是很分明也能够参考 PMD CLI options 的对应形容。

consoleOutput

是否将后果输入到终端(System.out)允许值为true|false

ignoreFailures

如果呈现了正告,是否容许持续构建,允许值为true|false

配置为否(false),在执行 build 的时候(build 工作中默认蕴含了 pmdMain 和 pmdTest),如果发现了代码有违反规定,将会中断构建过程;配置为是(true),将不会中断构建,只是输入报告信息。

maxFailures

进行构建前容许的最大失败次数。

incrementalAnalysis

是否开启增量剖析,允许值为 true|false。在 pmd docs Incremental Analysis 中详细描述了增量剖析的相干信息。简略来说,开启了增量剖析,PMD 会缓存剖析数据和后果,后续剖析仅查看那些新的 / 已更改的文件,以此显著缩小剖析的工夫,在 Gradle 中,这个性能应用 PMD6.0.0 及以上版本才有。

然而有一些状况会导致增量剖析的缓存生效:应用 PMD 的版本产生了变动;应用的规定集已更改;被剖析的代码的类门路已更改;被剖析代码依赖的库的类门路已更改。具体参考 When is the cache invalidated?

在以上前提下,即便切换分支缓存也是无效的,甚至还反对在不同的机器重复使用缓存文件。参考 Can I reuse a cache created on branch A for analyzing my project on branch B? 和 Can I reuse a cache file across different machines?

reportsDir

报告生成的门路。

ruleSetFiles

要应用的自定义规定集文件门路,能够在 files() 中填多个门路。

ruleSetFiles = files("config/pmd/myRuleSet.xml")

ruleSetConfig

ruleSetFiles 的作用一样,不过只能填一个文件门路。

ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")

ruleSets

指定应用的规定集,默认值为 ["category/java/errorprone.xml"]。 倡议如果配置了 ruleSetFiles 或者 ruleSetConfig,就将ruleSets 配置为空(ruleSets = []),免得相互烦扰,官网文档 Custom ruleset 给出的例子也是如此。

ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]

rulesMinimumPriority

每个规定都有个优先级,是从 1 到 5 的整数,其中 1 是最高优先级,参考 Message and priority overriding,每个规定的优先级参考 [Java Rules。rulesMinimumPriority 的作用是配置报告的最低优先级,低于这个优先级的规定将被疏忽。比方配置rulesMinimumPriority = 4,优先级为 5 的规定将被疏忽。

sourceSets

作为 checkbuild 工作的一部分进行剖析的源代码汇合,配置形式参考 SourceSet。

targetJdk

PMD 应用的 JDK 版本。有些规定可能会要求 JDK 的最低或者最高版本,具体要求参考 Java Rules。

threads

PMD 运行时应用的线程数。

toolVersion

要应用的 PMD 的版本。

为我的项目自定义适合的规定集

规定分类和查找

PMD 能检测的语音有很多种(前面内容以 Java 为例),针对不同的语音,PMD 内置了很多检测规定,并归为了以下几个类别:

  1. 最佳实际(Best Practices):这些规定执行广泛承受的最佳实际。
  2. 代码格调(Code Style):这些规定强制执行特定的编码格调。
  3. 设计(Design):帮忙您发现设计问题的规定。
  4. 文档(Documentation):这些规定与代码文档无关。
  5. 容易出错(Error Prone):用于检测损坏、极度凌乱或容易呈现运行时谬误的结构的规定。
  6. 多线程(Multithreading):这些是在解决多个执行线程时标记问题的规定。
  7. 性能(Performance):标记次优代码的规定。
  8. 安全性(Security):标记潜在安全漏洞的规定。

在 Java Rules 列出了所有相干的规定,点击蓝色字符能够跳转到规定的详细描述页面。

下图是规定 AbstractClassWithoutAbstractMethod 文档形容的信息,其余规定的形容可能还会蕴含 JDK 版本的要求,其余可配置属性等等。

须要留神有的规定可能被标记为 Deprecated 代表被弃用了。

配置规定集

咱们能够编辑 XML 格局的规定集文件,指定咱们我的项目要执行的规定,参考 Making rulesets。上面是没有蕴含任何规定的规定集文件的模版。

<?xml version="1.0"?>

<ruleset name="Custom Rules"
    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 custom rules
    </description>


    <!-- Your rules will come here -->

</ruleset>

从上文咱们能够晓得 PMD 内置的每个规定都会提供援用实例,咱们 援用单个规定 的时候,只须要将示例的 XML 代码复制到规定集文件中即可。

<rule ref="category/java/errorprone.xml/EmptyCatchBlock" />

ref 中填写的门路 category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod 咱们能够显著看到它是依照 内置规定集文件门路 / 规定名称的格局 组织的,一个内置规定集文件对应了一个分类。

咱们能够援用内置规定集文件实现 批量引入分类下的所有规定 ,每个分类对应的 XML 文件名能够参考 GitHub pmd-java resources。再通过exclude 指定规定的名称来排除某些规定。

<rule ref="category/java/codestyle.xml">
    <exclude name="WhileLoopsMustUseBraces"/>
    <exclude name="IfElseStmtsMustUseBraces"/>
</rule>

咱们能够应用 exclude-pattern 排除某些文件,使其不被 PMD 查看,也能够应用 include-pattern 蕴含的形式。如果两种形式都蕴含雷同的文件,最终这个文件会被 PMD 查看。

<?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 ruleset</description>

    <exclude-pattern>.*/some/package/.*</exclude-pattern>
    <exclude-pattern>.*/some/other/package/FunkyClassNamePrefix.*</exclude-pattern>
    <include-pattern>.*/some/package/ButNotThisClass.*</include-pattern>

    <!-- Rules here ... -->

</ruleset>

规定集文件编辑好后,应用 ruleSetFiles 或者 ruleSetConfig 配置门路。比方上面配置的意思是指向了我的项目根目录下的/code-analysis/pmd/rulesets/custom-rule.xml

ruleSetFiles = files("${project.rootDir}/code-analysis/pmd/rulesets/custom-rule.xml"

配置规定

规定援用的同时,咱们能够笼罩其原有的一些配置,比方提醒音讯 message 和优先级priority

<rule ref="category/java/errorprone.xml/EmptyCatchBlock"
      message="Empty catch blocks should be avoided" >
      <priority>5</priority>
</rule>

某些规定可能有特定的属性,咱们也能够将其笼罩。这些特定的属性 Java Rules 中都有提供,比方上面这个例子参考 NPathComplexity。

<rule ref="category/java/design.xml/NPathComplexity">
    <properties>
        <property name="reportLevel" value="150"/>
    </properties>
</rule>

有些属性能够提供多个值,这种状况下能够通过分隔符来提供,比方竖线(|)或逗号(,)。

<property name="legalCollectionTypes"
          value="java.util.ArrayList|java.util.Vector|java.util.HashMap"/>

克制正告

有时候 PMD 可能会产生误报,这种时候咱们能够通过克制正告让 PMD 跳过对这些代码的查看。

从 Java 1.5 开始能够应用注解 @SuppressWarnings 来标记类或者办法。

  • @SuppressWarnings('PMD')克制所有 PMD 的正告。
  • @SuppressWarnings("PMD.UnusedLocalVariable")克制规定 UnusedLocalVariable 的正告。
  • @SuppressWarnings({"PMD.UnusedLocalVariable", "PMD.UnusedPrivateMethod"})抑规定 UnusedLocalVariableUnusedPrivateMethod的正告。
  • @SuppressWarnings("unused")JDK 外面的 unusedPMD 也恪守,克制所有跟未应用相干的正告。比方:UnusedLocalVariableUnusedPrivateMethod

在正告提醒的代码行的开端加上正文 // NOPMD 也能够克制这一行引起的正告,参考 NOPMD。

在规定集文件中也能够配置要克制正告的文件,匹配的形式能够是正则表达式或者 XPath,具体能够理解 The property violationSuppressRegex 和 The property violationSuppressXPath。

第三方规定集

除了 PMD 内置的规定集,咱们还能够引入第三方规定集。在 3rd party rulesets 中列出了一些,还有阿里 Java 开发标准 p3c 也基于 PMD 开发一套规定集,从它的 pom.xml 能够理解到是基于 PMD6.15.0版本。

参考 Dependency management 引入规定集依赖,在规定集配置中引入提供的规定即可。

dependencies {pmd "com.alibaba.p3c:p3c-pmd:2.1.1"}

须要留神的是,第三方的规定集很可能没有依照 PMD 内置规定集那样分类,它们提供的规定配置文件目录也可能不一样,比方 p3c 的规定配置文件都在 /resources/rulesets/ 目录下并单独定义了一套分类。

其余技巧

PMD 的最新官网文档地址是:https://docs.pmd-code.org/latest/pmd_userdocs_tools.html。链接中的 latest 对应了版本号,指向的是以后最新版本,如果想查看其余版本的文档,批改为对应的版号即可。比方 6.39.0 版本的链接为:https://docs.pmd-code.org/pmd-doc-6.39.0/index.html。不过可能只有比拟新的一些版本能力看到对应的文档。

官网提供了一个 PMD 的最佳实际能够理解下。

PMD 还有跟特定语言相干的文档,比方 Java support,外面有反对的 JDK 版本等信息。

如果应用过程中遇到了问题,能够参考 Getting Help 从这些网站外面寻找帮忙 github discussions、github issues、stackoverflow tagged pmd。

PMD 官网文档还提供了 Copy/Paste Detector (CPD)关信息,CPD 能够用于检测反复代码。还提及了 Duplicate Code 教咱们遇到反复代码如何打消,以及一个对于设计模式的网站 Design Patterns。

对于 PMD 这个名字,并没有非凡的含意,作者纯正只是感觉这几个字母放一起作为名称挺好的,来自 What does ‘PMD’ mean?

参考

GitHub – pmd

PMD – docs

Gradle – The PMD Plugin

正文完
 0