Java 代码检查工具之PMD入门使用详细教程

时间:2022-10-18 21:09:39

介绍

PMD是一个静态源代码分析器。它发现了常见的编程缺陷,如未使用的变量、空捕获块、不必要的对象创建等等。

官网:点这里
官方文档:点这里

使用方式

1、使用插件的方式

下载:File -> Settings -> Plugins -> Marketplace 搜索 “PMDPlugin” ,下载插件。

使用方法:在代码编辑框或Project 窗口的文件夹、包、文件右键,选择“Run PMD”->“Pre Defined”->“All”,对指定的文件夹、包、文件进行分析,分析结果在控制台输出。

2、maven项目引入依赖的方式

pom.xml:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6.  
  7. <groupId>com.keafmd</groupId>
  8. <artifactId>pdm-test01</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10.  
  11. <!--<dependencies>
  12. <dependency>
  13. <groupId>org.apache.maven.plugins</groupId>
  14. <artifactId>maven-pmd-plugin</artifactId>
  15. <version>3.14.0</version>
  16. <type>maven-plugin</type>
  17. </dependency>
  18.  
  19. </dependencies>-->
  20.  
  21. <!-- 用于生成错误到代码内容的链接 -->
  22. <reporting>
  23. <plugins>
  24. <plugin>
  25. <groupId>org.apache.maven.plugins</groupId>
  26. <artifactId>maven-pmd-plugin</artifactId>
  27. <version>3.14.0</version>
  28.  
  29. </plugin>
  30. </plugins>
  31. </reporting>
  32.  
  33. </project>

mvn 命令执行

在项目目录打开cmd窗口,输入以下命令:

  1. mvn pmd:pmd

Java 代码检查工具之PMD入门使用详细教程

分析结果为pmd.html文件,在项目的target下的site目录下:

Java 代码检查工具之PMD入门使用详细教程

Java 代码检查工具之PMD入门使用详细教程

分析结果显示内容:

Java 代码检查工具之PMD入门使用详细教程

3、pmd 命令行的方式

  1. pmd -d 源代码路径 -f xml(结果输出格式) -r 结果保存所在目录及名称 -R rulesets/java/unusedcode.xml

例子:

Java 代码检查工具之PMD入门使用详细教程

结果存放在制定文件目录下,格式也为命令语句指定的:

Java 代码检查工具之PMD入门使用详细教程

检测结果内容:

Java 代码检查工具之PMD入门使用详细教程

4、Java API的方式 *

官方文档

需要先引入maven依赖

项目结构

Java 代码检查工具之PMD入门使用详细教程

测试代码

Test01:

  1. package com.keafmd.test01;
  2.  
  3. /**
  4. * Keafmd
  5. *
  6. * @ClassName: Test01
  7. * @Description: 测试1
  8. * @author: 牛哄哄的柯南
  9. * @Date: 2021-03-15 15:29
  10. * @Blog: https://keafmd.blog.csdn.net/
  11. */
  12. public class Test01 {
  13. public static void main(String[] args) {
  14. int a =100;
  15. int b=29;
  16. String s ="abc";
  17. System.out.println("hello!");
  18. }
  19.  
  20. }

Test02:

  1. package com.keafmd.test02;
  2.  
  3. /**
  4. * Keafmd
  5. *
  6. * @ClassName: Test02
  7. * @Description:
  8. * @author: 牛哄哄的柯南
  9. * @Date: 2021-03-15 15:30
  10. * @Blog: https://keafmd.blog.csdn.net/
  11. */
  12. public class Test02 {
  13. public static void main(String[] args) {
  14. boolean flag=true;
  15. while(flag){
  16. flag=false;
  17. }
  18. System.out.println("123");
  19. int a =100;
  20. int b=29;
  21. String s ="abc";
  22. System.out.println("hello!");
  23. }
  24. }

pmdArgs方式

命令行接口的方式
最简单的方法是使用与命令行相同的接口调用PMD

Example :

  1. package com.keafmd;
  2. import net.sourceforge.pmd.PMD;
  3. /**
  4. * Keafmd
  5. *
  6. * @ClassName: Example
  7. * @Description:
  8. * @author: 牛哄哄的柯南
  9. * @Date: 2021-03-15 15:51
  10. * @Blog: https://keafmd.blog.csdn.net/
  11. */
  12. public class Example {
  13. public static void main(String[] args) {
  14. String[] pmdArgs = {
  15. "-d", "D:/javaworkspace/pdm-test02/src",
  16. "-R", "rulesets/java/quickstart.xml",
  17. "-f", "xml",
  18. "-r", "D:/pmdreport/pmd-report.xml"
  19. };
  20. PMD.main(pmdArgs);
  21. }
  22. }

PMDConfiguration方式

PmdExample:

  1. package com.keafmd;
  2. import net.sourceforge.pmd.PMD;
  3. import net.sourceforge.pmd.PMDConfiguration;
  4. /**
  5. * Keafmd
  6. *
  7. * @ClassName: PmdExample
  8. * @Description:
  9. * @author: 牛哄哄的柯南
  10. * @Date: 2021-03-15 15:57
  11. * @Blog: https://keafmd.blog.csdn.net/
  12. */
  13.  
  14. public class PmdExample {
  15.  
  16. public static void main(String[] args) {
  17. PMDConfiguration configuration = new PMDConfiguration();
  18. configuration.setInputPaths("D:/javaworkspace/pdm-test/src");
  19. configuration.setRuleSets("rulesets/java/quickstart.xml");
  20. configuration.setReportFormat("html");
  21. configuration.setReportFile("D:/pmdreport/pmd-report.html");
  22.  
  23. PMD.doPMD(configuration);
  24. }
  25. }

Programmatically(拓展)

这使您能够更好地控制处理哪些文件,但也会更加复杂。您还可以提供自己的侦听器和呈现器。

1. 首先,我们创建一个PMDConfiguration。目前,这是指定规则集的唯一方法:

  1. PMDConfiguration configuration = new PMDConfiguration();
  2. configuration.setMinimumPriority(RulePriority.MEDIUM);
  3. configuration.setRuleSets("rulesets/java/quickstart.xml");

2. 为了支持类型解析,PMD还需要访问已编译的类和依赖项。这被称为“生长素路径”,并且在这里也进行了配置。注意:您可以指定由:关于Unix系统或;在Windows下。

  1. configuration.prependClasspath("/home/workspace/target/classes:/home/.m2/repository/my/dependency.jar");

3. 那我们需要一个规则工厂。这是使用配置创建的,同时考虑到最低优先级:

  1. RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);

4. PMD操作于DataSource。您可以收集自己的列表FileDataSource.

  1. List<DataSource> files = Arrays.asList(new FileDataSource(new File("/path/to/src/MyClass.java")));

5. 对于报告,您可以使用内置渲染器。XMLRenderer。注意,必须通过设置适当的Writer打电话start()。在pmd运行之后,您需要调用end()flush()。那么你的作者应该收到所有的输出。

  1. StringWriter rendererOutput = new StringWriter();
  2. Renderer xmlRenderer = new XMLRenderer("UTF-8");
  3. xmlRenderer.setWriter(rendererOutput);
  4. xmlRenderer.start();

6. 创建一个RuleContext。这是上下文实例,在规则实现中是可用的。注意:当在多线程模式下运行时(这是默认的),规则上下文实例将被克隆到每个线程。

  1. RuleContext ctx = new RuleContext();

7. 可以选择注册报表侦听器。这样你就可以对发现的违规行为立即做出反应。您也可以使用这样的侦听器来实现您自己的呈现器。侦听器必须实现接口。ThreadSafeReportListener并且可以通过ctx.getReport().addListener(...).

  1. ctx.getReport().addListener(new ThreadSafeReportListener() {
  2. public void ruleViolationAdded(RuleViolation ruleViolation) {
  3. }
  4. public void metricAdded(Metric metric) {
  5. }

8. 现在,所有的准备工作都完成了,PMD可以执行了。这是通过调用PMD.processFiles(...)。此方法调用接受配置、规则集工厂、要处理的文件、规则上下文和呈现器列表。如果不想使用任何渲染器,请提供一个空列表。注意:需要显式关闭辅助路径。否则,类或JAR文件可能会保持打开状态,并且文件资源会泄漏。

  1. try {
  2. PMD.processFiles(configuration, ruleSetFactory, files, ctx,
  3. Collections.singletonList(renderer));
  4. } finally {
  5. ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
  6. if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
  7. ((ClasspathClassLoader) auxiliaryClassLoader).close();
  8. }
  9. }

9. 呼叫后,您需要完成渲染器end()flush()。然后,您可以检查呈现的输出。

  1. renderer.end();
  2. renderer.flush();
  3. System.out.println("Rendered Report:");
  4. System.out.println(rendererOutput.toString());

下面是一个完整的例子:

  1. import java.io.IOException;
  2. import java.io.StringWriter;
  3. import java.io.Writer;
  4. import java.nio.file.FileSystems;
  5. import java.nio.file.FileVisitResult;
  6. import java.nio.file.Files;
  7. import java.nio.file.Path;
  8. import java.nio.file.PathMatcher;
  9. import java.nio.file.SimpleFileVisitor;
  10. import java.nio.file.attribute.BasicFileAttributes;
  11. import java.util.ArrayList;
  12. import java.util.Collections;
  13. import java.util.List;
  14.  
  15. import net.sourceforge.pmd.PMD;
  16. import net.sourceforge.pmd.PMDConfiguration;
  17. import net.sourceforge.pmd.RuleContext;
  18. import net.sourceforge.pmd.RulePriority;
  19. import net.sourceforge.pmd.RuleSetFactory;
  20. import net.sourceforge.pmd.RuleViolation;
  21. import net.sourceforge.pmd.RulesetsFactoryUtils;
  22. import net.sourceforge.pmd.ThreadSafeReportListener;
  23. import net.sourceforge.pmd.renderers.Renderer;
  24. import net.sourceforge.pmd.renderers.XMLRenderer;
  25. import net.sourceforge.pmd.stat.Metric;
  26. import net.sourceforge.pmd.util.ClasspathClassLoader;
  27. import net.sourceforge.pmd.util.datasource.DataSource;
  28. import net.sourceforge.pmd.util.datasource.FileDataSource;
  29.  
  30. public class PmdExample2 {
  31.  
  32. public static void main(String[] args) throws IOException {
  33. PMDConfiguration configuration = new PMDConfiguration();
  34. configuration.setMinimumPriority(RulePriority.MEDIUM);
  35. configuration.setRuleSets("rulesets/java/quickstart.xml");
  36. configuration.prependClasspath("/home/workspace/target/classes");
  37. RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);
  38.  
  39. List<DataSource> files = determineFiles("/home/workspace/src/main/java/code");
  40.  
  41. Writer rendererOutput = new StringWriter();
  42. Renderer renderer = createRenderer(rendererOutput);
  43. renderer.start();
  44.  
  45. RuleContext ctx = new RuleContext();
  46.  
  47. ctx.getReport().addListener(createReportListener()); // alternative way to collect violations
  48.  
  49. try {
  50. PMD.processFiles(configuration, ruleSetFactory, files, ctx,
  51. Collections.singletonList(renderer));
  52. } finally {
  53. ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
  54. if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
  55. ((ClasspathClassLoader) auxiliaryClassLoader).close();
  56. }
  57. }
  58.  
  59. renderer.end();
  60. renderer.flush();
  61. System.out.println("Rendered Report:");
  62. System.out.println(rendererOutput.toString());
  63. }
  64.  
  65. private static ThreadSafeReportListener createReportListener() {
  66. return new ThreadSafeReportListener() {
  67. @Override
  68. public void ruleViolationAdded(RuleViolation ruleViolation) {
  69. System.out.printf("%-20s:%d %s%n", ruleViolation.getFilename(),
  70. ruleViolation.getBeginLine(), ruleViolation.getDescription());
  71. }
  72.  
  73. @Override
  74. public void metricAdded(Metric metric) {
  75. // ignored
  76. }
  77. };
  78. }
  79.  
  80. private static Renderer createRenderer(Writer writer) {
  81. XMLRenderer xml = new XMLRenderer("UTF-8");
  82. xml.setWriter(writer);
  83. return xml;
  84. }
  85.  
  86. private static List<DataSource> determineFiles(String basePath) throws IOException {
  87. Path dirPath = FileSystems.getDefault().getPath(basePath);
  88. PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
  89.  
  90. List<DataSource> files = new ArrayList<>();
  91.  
  92. Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
  93. @Override
  94. public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
  95. if (matcher.matches(path.getFileName())) {
  96. System.out.printf("Using %s%n", path);
  97. files.add(new FileDataSource(path.toFile()));
  98. } else {
  99. System.out.printf("Ignoring %s%n", path);
  100. }
  101. return super.visitFile(path, attrs);
  102. }
  103. });
  104. System.out.printf("Analyzing %d files in %s%n", files.size(), basePath);
  105. return files;
  106. }
  107. }

分析结果

分析结果会根据指定格式输出在指定文件目录下。

图形界面

检测

D:\MyFile\Tool\pmd-bin-6.32.0\bin 目录下打开cmd窗口输入:

  1. cpdgui.bat

Java 代码检查工具之PMD入门使用详细教程

自定义规则

Java 代码检查工具之PMD入门使用详细教程

D:\MyFile\Tool\pmd-bin-6.32.0\bin 目录下打开cmd窗口输入:

  1. designer.bat

Java 代码检查工具之PMD入门使用详细教程

自定义规则:不能有变量为keafmd的String类型的变量

String keafmd; //这样就是不合法的。

Source:

  1. public class KeepingItSerious {
  2.  
  3. Delegator keafmd; // FieldDeclaration
  4.  
  5. public void method() {
  6. String keafmd; // LocalVariableDeclaration
  7. }
  8.  
  9. }

导出的自定义规则:

  1. <rule name="myrule"
  2. language="java"
  3. message="不能有变量为keafmd的String类型的变量"
  4. class="net.sourceforge.pmd.lang.rule.XPathRule">
  5. <description>
  6. 自定义规则
  7. </description>
  8. <priority>3</priority>
  9. <properties>
  10. <property name="version" value="2.0"/>
  11. <property name="xpath">
  12. <value>
  13. <![CDATA[
  14. //VariableDeclaratorId[@Image = "keafmd" and ../../Type[@TypeImage = "String"]]
  15. ]]>
  16. </value>
  17. </property>
  18. </properties>
  19. </rule>

到此这篇关于Java 代码检查工具之PMD入门使用详细教程的文章就介绍到这了,更多相关Java 代码检查工具PMD内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://keafmd.blog.csdn.net/article/details/115069959