提高代码质量!详解在Gradle项目中使用PMD的正确姿势

当今的软件开发需要使用许多不同的工具和技术来确保代码质量和稳定性。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的配置属性或者功能不一定在每个版本都有。

image-20230409124759078

通过页面左上角选了其他版本后跳转的地址是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规则的类会给出完整的跳转路径以及规则提示信息。

image-20230409135016234

最后还会给出一个报告的地址,内容包含了输出到终端的信息。Problem列出了规则的提示,点击可以跳转到PMD规则描述文档对应的位置。

image-20230409135722137

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列出了所有相关的规则,点击蓝色字符可以跳转到规则的详细描述页面。

image-20230409200607724

下图是规则AbstractClassWithoutAbstractMethod文档描述的信息,其他规则的描述可能还会包含JDK版本的要求,其他可配置属性等等。

image-20230409200833756

需要注意有的规则可能被标记为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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7666.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国内智慧城市标准是怎样的?

我国智慧城市标准化工作的历史可以回溯至2013 年&#xff0c;相关工作也得到了国家标准委、中央网信办、发展改革委、工业和信息化部等部门的高度关注和支持&#xff0c;在标准化协调机制、国家标准研制实施等方面取得了积极进展。 北京智汇云舟科技有限公司成立于2012年&#…

【vSphere | Python】vSphere Automation SDK for Python Ⅲ—— vCenter Datacenter APIs

目录5. vCenter Datacenter APIs操作5.1 Create Datacenter5.2 List Datacenter5.3 Get Datacenter5.4 Delete Datacenter参考资料5. vCenter Datacenter APIs 数据中心服务&#xff08;Datacenter service&#xff09;提供管理 vCenter Server 中数据中心的操作。 操作 Cre…

新加坡电商系统上线指南

如果您正在考虑在新加坡开展电子商务业务并准备上线您的电商网站&#xff0c;以下是一些指南和建议&#xff0c;可以帮助您成功地启动和运营您的电商业务&#xff1a; 确认您的业务模式和目标市场。在上线之前&#xff0c;您需要确定您的业务模式&#xff0c;例如是B2B&#xf…

Python 进阶指南(编程轻松进阶):六、编写 Python 风格的代码

原文&#xff1a;http://inventwithpython.com/beyond/chapter6.html 强大对于编程语言来说是一个没有意义的形容词。每种编程语言都称自己长处。官方 Python 教程开头就说 Python 是一种简单易学、功能强大的编程语言。但是没有一种语言可以做另一种语言不能做的算法&#xff…

Robosense激光雷达Linux配置

文章目录1.1 速腾rs16连接&#xff1a;1.2 网络配置1&#xff09;官方说明2&#xff09;设置网络3&#xff09;检查是否连接成功2.1 激光雷达ROS包下载/编译1)下载ROS包2&#xff09;安装libpcap依赖3&#xff09;修改编译模式4&#xff09;config文件配置5&#xff09;编译并运…

AI-TestOps —— 软件测试工程师的一把利剑

写在前面软件测试的前世今生测试工具开始盛行AI-TestOps 云平台● AI-TestOps 功能模块● AI-TestOps 自动化测试流程写在前面 最近偶然间看到一句话&#xff1a;“软件测试是整个 IT 行业中最差的岗位”。这顿时激起了我对软件测试领域的兴趣&#xff0c;虽然之前未涉及过软件…

《Flutter进阶》flutter升级空安全遇到的一些问题及解决思路

空安全出来挺久了&#xff0c;由于业务需求较紧&#xff0c;一直没时间去升级空安全&#xff0c;最近花了几天去升级&#xff0c;发现其实升级也挺简单的&#xff0c;不要恐惧&#xff0c;没有想象中的多BUG。 flutter版本从1.22.4升到3.0.5&#xff1b; compileSdkVersion从1…

日撸 Java 三百行day11-13

文章目录说明day11-day12 顺序表1.面向过程面向对象区别2.代码2.1 面向过程2.2 面向对象day13 链表1.成员内部类2.链表的插入删除3.代码说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了…

【51单片机】:LED任务及汇编解释任务

学习目标&#xff1a; 1、用汇编或者c语言实现D1 D3 D5 D7 为一组 &#xff1b;D2 D4 D6 D8 为一组 &#xff0c;两组实现 1&#xff09;一组亮约一秒 另一组灭一秒&#xff0c;这样的互闪现象五次后 25分 2&#xff09;所有灯灭约一秒后&#xff0c; …

关于ChatGPT的一些随笔

大家好&#xff0c;我是老三&#xff0c;最近几个月关于ChatGPT的信息可以说是铺天盖地。 “王炸&#xff0c;ChatGPT……” “xxx震撼发布……” “真的要失业了&#xff0c;xxx来袭……” “普通如何利用ChatGPT……” …… 不过老三前一阵比较忙&#xff0c;对ChatGPT…

ElasticSearch简介

第一章 ElasticSearch简介 1.1 什么是ElasticSearch Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b;本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB…

【数据结构与算法】树与二叉树

目录一.树1.树的定义2.结点的分类与关系3.树的相关概念4.树的表示方法二.二叉树1.二叉树的定义2.特殊二叉树3.二叉树的性质4.二叉树的顺序结构5.二叉树的链式结构(1)链式结构的创建(2)结点的创建(3)二叉树的手动构建(4)前中后序遍历(5)二叉树结点个数(6)二叉树的高度(7)第k层的…

Docker目录迁移

介绍 在docker的使用中随着下载镜像越来越多&#xff0c;构建镜像、运行容器越来越多, 数据目录必然会逐渐增大&#xff1b;当所有docker镜像、容器对磁盘的使用达到上限时&#xff0c;就需要对数据目录进行迁移。 如何避免&#xff1a; 1.在安装前对/var/lib/docker&#x…

如何3步精读《PMBOK指南》(含PMP备考资料)

初学者学习《PMBOK指南》的确有点吃亏&#xff0c;比不得那些项目管理专业以及相关专业的毕业生&#xff0c;哪怕只稍微接触过项目的都比初学者强。 所以&#xff0c;有计划性的阅读就显得尤为重要&#xff0c;要克服的不仅是阅读上的枯燥&#xff0c;还有专业知识的理解&…

Java——JDK动态代理

1.动态代理 1.1什么是动态代理&#xff1f; 动态代理(理解) 基于反射机制 举个例子&#xff0c;生活中一般在打官司的时候都会请代理律师&#xff0c;为什么要请律师呢&#xff1f;是因为开庭的时候大部人对于打官司没有经验&#xff0c;只会说出自己案件的陈述&#xff0c;并不…

软硬皆施,WMS仓库管理系统+PDA,实现效率狂飙

人工经验Excel表格&#xff0c;是传统第三方仓储企业常用的管理模式。在这种管理模式下&#xff0c;对仓库员工的Excel操作能力、业务经验和工作素养要求极高。一旦员工的经验能力不足&#xff0c;就会导致仓库业务运行不顺畅&#xff0c;效率低下&#xff0c;而员工也会因长时…

【MySQL】基于GTID的半同步主从复制(实践)

一、GTID简介 什么是GTID? 全局事务标识符GTID的全称为Global Transaction Identifier&#xff0c;是在整个复制环境中对一个事务的唯一标识。 它是MySQL 5.6加入的一个强大特性&#xff0c;目的在于能够实现主从自动定位和切换&#xff0c;而不像以前需要指定文件和位置。 …

ArduPilot飞控之DIY-F450计划

ArduPilot飞控之DIY-F450计划1. 历史2. 源由3. 计划3.1 硬件3.2 软件4. 动手4.1 接线4.1.1 ELRS nano接收机4.1.2 BN880 GPS模块4.1.3 Radio Telemetry4.2 配置4.2.1 选择四轴机型4.2.2 电源参数调整4.2.3 校准加速度计4.2.4 校准磁力计4.2.5 遥控器校准4.2.6 电机设置4.2.7 电…

企业电子招投标采购系统——功能模块功能描述+数字化采购管理 采购招投标

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

Melis4.0[D1s]:4.测试笔记 - 内嵌的显示命令

文章目录1.配置将显示测试源码包含进工程&#xff08;默认是包含了&#xff09;2.不要启动melis桌面系统3.开始测试3.1 disp 命令3.1.1 disp不带参数时&#xff0c;打印显示信息&#xff1a;3.1.2 disp -c 0 8 测试4种颜色3.2 disp_layer_cfg 命令3.3 disp_mem 对显示内存写入内…