信息发布→ 登录 注册 退出

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

发布时间:2026-01-11

点击量:
目录
  • 介绍
  • 使用方式
    • 1、使用插件的方式
    • 2、maven项目引入依赖的方式
    • 3、pmd 命令行的方式
    • 4、Java API的方式 *
      • 项目结构
      • 测试代码
      • pmdArgs方式
      • PMDConfiguration方式
      • Programmatically(拓展)
      • 分析结果
  • 图形界面
    • 检测
      • 自定义规则

      介绍

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

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

      使用方式

      1、使用插件的方式

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

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

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

      pom.xml:

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
      
       <groupId>com.keafmd</groupId>
       <artifactId>pdm-test01</artifactId>
       <version>1.0-SNAPSHOT</version>
      
       <!--<dependencies>
       <dependency>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-pmd-plugin</artifactId>
       <version>3.14.0</version>
       <type>maven-plugin</type>
       </dependency>
      
       </dependencies>-->
      
       <!-- 用于生成错误到代码内容的链接 -->
       <reporting>
       <plugins>
       <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-pmd-plugin</artifactId>
       <version>3.14.0</version>
       
       </plugin>
       </plugins>
       </reporting>
      
      </project>

      mvn 命令执行

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

      mvn pmd:pmd

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

      分析结果显示内容:

      3、pmd 命令行的方式

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

      例子:

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

      检测结果内容:

      4、Java API的方式 *

      官方文档

      需要先引入maven依赖

      项目结构

      测试代码

      Test01:

      package com.keafmd.test01;
      
      
      /**
       * Keafmd
       *
       * @ClassName: Test01
       * @Description: 测试1
       * @author: 牛哄哄的柯南
       * @Date: 2025-03-15 15:29
       * @Blog: https://keafmd.blog.csdn.net/
       */
      public class Test01 {
       public static void main(String[] args) {
       int a =100;
       int b=29;
       String s ="abc";
       System.out.println("hello!");
       }
      
      
      }

      Test02:

      package com.keafmd.test02;
      
      /**
       * Keafmd
       *
       * @ClassName: Test02
       * @Description:
       * @author: 牛哄哄的柯南
       * @Date: 2025-03-15 15:30
       * @Blog: https://keafmd.blog.csdn.net/
       */
      public class Test02 {
       public static void main(String[] args) {
       boolean flag=true;
       while(flag){
       flag=false;
       }
       System.out.println("123");
       int a =100;
       int b=29;
       String s ="abc";
       System.out.println("hello!");
       }
      }

      pmdArgs方式

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

      Example :

      package com.keafmd;
      import net.sourceforge.pmd.PMD;
      /**
       * Keafmd
       *
       * @ClassName: Example
       * @Description:
       * @author: 牛哄哄的柯南
       * @Date: 2025-03-15 15:51
       * @Blog: https://keafmd.blog.csdn.net/
       */
      public class Example {
       public static void main(String[] args) {
       String[] pmdArgs = {
       "-d", "D:/javaworkspace/pdm-test02/src",
       "-R", "rulesets/java/quickstart.xml",
       "-f", "xml",
       "-r", "D:/pmdreport/pmd-report.xml"
       };
       PMD.main(pmdArgs);
       }
      }

      PMDConfiguration方式

      PmdExample:

      package com.keafmd;
      import net.sourceforge.pmd.PMD;
      import net.sourceforge.pmd.PMDConfiguration;
      /**
       * Keafmd
       *
       * @ClassName: PmdExample
       * @Description:
       * @author: 牛哄哄的柯南
       * @Date: 2025-03-15 15:57
       * @Blog: https://keafmd.blog.csdn.net/
       */
      
      public class PmdExample {
      
       public static void main(String[] args) {
       PMDConfiguration configuration = new PMDConfiguration();
       configuration.setInputPaths("D:/javaworkspace/pdm-test/src");
       configuration.setRuleSets("rulesets/java/quickstart.xml");
       configuration.setReportFormat("html");
       configuration.setReportFile("D:/pmdreport/pmd-report.html");
      
       PMD.doPMD(configuration);
       }
      }

      Programmatically(拓展)

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

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

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

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

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

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

      RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);

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

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

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

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

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

      RuleContext ctx = new RuleContext();

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

      ctx.getReport().addListener(new ThreadSafeReportListener() {
       public void ruleViolationAdded(RuleViolation ruleViolation) {
       }
       public void metricAdded(Metric metric) {
       }

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

      try {
       PMD.processFiles(configuration, ruleSetFactory, files, ctx,
       Collections.singletonList(renderer));
      } finally {
       ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
       if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
       ((ClasspathClassLoader) auxiliaryClassLoader).close();
       }
      }

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

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

      下面是一个完整的例子:

      import java.io.IOException;
      import java.io.StringWriter;
      import java.io.Writer;
      import java.nio.file.FileSystems;
      import java.nio.file.FileVisitResult;
      import java.nio.file.Files;
      import java.nio.file.Path;
      import java.nio.file.PathMatcher;
      import java.nio.file.SimpleFileVisitor;
      import java.nio.file.attribute.BasicFileAttributes;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      
      import net.sourceforge.pmd.PMD;
      import net.sourceforge.pmd.PMDConfiguration;
      import net.sourceforge.pmd.RuleContext;
      import net.sourceforge.pmd.RulePriority;
      import net.sourceforge.pmd.RuleSetFactory;
      import net.sourceforge.pmd.RuleViolation;
      import net.sourceforge.pmd.RulesetsFactoryUtils;
      import net.sourceforge.pmd.ThreadSafeReportListener;
      import net.sourceforge.pmd.renderers.Renderer;
      import net.sourceforge.pmd.renderers.XMLRenderer;
      import net.sourceforge.pmd.stat.Metric;
      import net.sourceforge.pmd.util.ClasspathClassLoader;
      import net.sourceforge.pmd.util.datasource.DataSource;
      import net.sourceforge.pmd.util.datasource.FileDataSource;
      
      public class PmdExample2 {
      
       public static void main(String[] args) throws IOException {
       PMDConfiguration configuration = new PMDConfiguration();
       configuration.setMinimumPriority(RulePriority.MEDIUM);
       configuration.setRuleSets("rulesets/java/quickstart.xml");
       configuration.prependClasspath("/home/workspace/target/classes");
       RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(configuration);
      
       List<DataSource> files = determineFiles("/home/workspace/src/main/java/code");
      
       Writer rendererOutput = new StringWriter();
       Renderer renderer = createRenderer(rendererOutput);
       renderer.start();
      
       RuleContext ctx = new RuleContext();
      
       ctx.getReport().addListener(createReportListener()); // alternative way to collect violations
      
       try {
       PMD.processFiles(configuration, ruleSetFactory, files, ctx,
       Collections.singletonList(renderer));
       } finally {
       ClassLoader auxiliaryClassLoader = configuration.getClassLoader();
       if (auxiliaryClassLoader instanceof ClasspathClassLoader) {
       ((ClasspathClassLoader) auxiliaryClassLoader).close();
       }
       }
      
       renderer.end();
       renderer.flush();
       System.out.println("Rendered Report:");
       System.out.println(rendererOutput.toString());
       }
      
       private static ThreadSafeReportListener createReportListener() {
       return new ThreadSafeReportListener() {
       @Override
       public void ruleViolationAdded(RuleViolation ruleViolation) {
       System.out.printf("%-20s:%d %s%n", ruleViolation.getFilename(),
        ruleViolation.getBeginLine(), ruleViolation.getDescription());
       }
      
       @Override
       public void metricAdded(Metric metric) {
       // ignored
       }
       };
       }
      
       private static Renderer createRenderer(Writer writer) {
       XMLRenderer xml = new XMLRenderer("UTF-8");
       xml.setWriter(writer);
       return xml;
       }
      
       private static List<DataSource> determineFiles(String basePath) throws IOException {
       Path dirPath = FileSystems.getDefault().getPath(basePath);
       PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
      
       List<DataSource> files = new ArrayList<>();
      
       Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
       @Override
       public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
       if (matcher.matches(path.getFileName())) {
        System.out.printf("Using %s%n", path);
        files.add(new FileDataSource(path.toFile()));
       } else {
        System.out.printf("Ignoring %s%n", path);
       }
       return super.visitFile(path, attrs);
       }
       });
       System.out.printf("Analyzing %d files in %s%n", files.size(), basePath);
       return files;
       }
      }

      分析结果

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

      图形界面

      检测

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

      cpdgui.bat

      自定义规则

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

      designer.bat

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

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

      Source:

      public class KeepingItSerious {
      
       Delegator keafmd; // FieldDeclaration
      
       public void method() {
       String keafmd; // LocalVariableDeclaration
       }
      
      }

      导出的自定义规则:

      <rule name="myrule"
       language="java"
       message="不能有变量为keafmd的String类型的变量"
       class="net.sourceforge.pmd.lang.rule.XPathRule">
       <description>
      	自定义规则
       </description>
       <priority>3</priority>
       <properties>
       <property name="version" value="2.0"/>
       <property name="xpath">
       <value>
      <![CDATA[
      //VariableDeclaratorId[@Image = "keafmd" and ../../Type[@TypeImage = "String"]]
      ]]>
       </value>
       </property>
       </properties>
      </rule>
      在线客服
      服务热线

      服务热线

      4008888355

      微信咨询
      二维码
      返回顶部
      ×二维码

      截屏,微信识别二维码

      打开微信

      微信号已复制,请打开微信添加咨询详情!