PMD【 Java 代码检查工具】入门使用教程(超详细)

简介: PMD【 Java 代码检查工具】入门使用教程(超详细)

介绍


     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
1

1.png

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

2.png

3.png



分析结果显示内容:

4.png



3、pmd 命令行的方式


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

1

例子:

5.png


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


6.png


检测结果内容:


7.png

4、Java API的方式 *


官方文档


需要先引入maven依赖


项目结构

8.png


测试代码


Test01:


package com.keafmd.test01;
/**
 * Keafmd
 *
 * @ClassName: Test01
 * @Description: 测试1
 * @author: 牛哄哄的柯南
 * @Date: 2021-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: 2021-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: 2021-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: 2021-03-15 15:57
 * @Blog: https://keafmd.blog.csdn.net/
 */
public class PmdExample {
    public static void main(String[] args) {
  /*String fileName ;
        // 重新生成文件名(根据具体情况生成对应文件名)
        fileName = UUID.randomUUID()+"_"+"pmd-report.html";
        PMDConfiguration configuration = new PMDConfiguration();
        configuration.setInputPaths("D:/file");
        configuration.setRuleSets("rulesets/java/quickstart.xml");
        configuration.setReportFormat("html");
        configuration.setReportFile("D:/pmdreport/"+fileName);
        PMD.doPMD(configuration);*/
        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
1

9.png


自定义规则

10.png

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

11.png

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>

以上就是PMD【 Java 代码检查工具】入门使用教程(超详细)的全部内容。


相关文章
|
12天前
|
Java API 数据库
2025 年最新 Java 实操学习路线,从入门到高级应用详细指南
2025年Java最新实操学习路线,涵盖从环境搭建到微服务、容器化部署的全流程实战内容,助你掌握Java 21核心特性、Spring Boot 3.2开发、云原生与微服务架构,提升企业级项目开发能力,适合从入门到高级应用的学习需求。
229 0
|
21天前
|
前端开发 Java 数据库连接
帮助新手快速上手的 JAVA 学习路线最详细版涵盖从入门到进阶的 JAVA 学习路线
本Java学习路线涵盖从基础语法、面向对象、异常处理到高级框架、微服务、JVM调优等内容,适合新手入门到进阶,助力掌握企业级开发技能,快速成为合格Java开发者。
262 3
|
21天前
|
监控 Java API
2025 年全新出炉的 Java 学习路线:从入门起步到实操精通的详细指南
2025年Java学习路线与实操指南,涵盖Java 21核心特性、虚拟线程、Spring Boot 3、微服务、Spring Security、容器化部署等前沿技术,助你从入门到企业级开发进阶。
181 0
|
1月前
|
NoSQL Java 关系型数据库
Java 从入门到进阶完整学习路线图规划与实战开发最佳实践指南
本文为Java开发者提供从入门到进阶的完整学习路线图,涵盖基础语法、面向对象、数据结构与算法、并发编程、JVM调优、主流框架(如Spring Boot)、数据库操作(MySQL、Redis)、微服务架构及云原生开发等内容,并结合实战案例与最佳实践,助力高效掌握Java核心技术。
217 1
|
1月前
|
Java 测试技术 API
Java IO流(二):文件操作与NIO入门
本文详解Java NIO与传统IO的区别与优势,涵盖Path、Files类、Channel、Buffer、Selector等核心概念,深入讲解文件操作、目录遍历、NIO实战及性能优化技巧,适合处理大文件与高并发场景,助力高效IO编程与面试准备。
|
1月前
|
Java 编译器 API
Java Lambda表达式与函数式编程入门
Lambda表达式是Java 8引入的重要特性,简化了函数式编程的实现方式。它通过简洁的语法替代传统的匿名内部类,使代码更清晰、易读。本文深入讲解Lambda表达式的基本语法、函数式接口、方法引用等核心概念,并结合集合操作、线程处理、事件回调等实战案例,帮助开发者掌握现代Java编程技巧。同时,还解析了面试中高频出现的相关问题,助你深入理解其原理与应用场景。
|
1月前
|
安全 Java 数据库连接
2025 年最新 Java 学习路线图含实操指南助你高效入门 Java 编程掌握核心技能
2025年最新Java学习路线图,涵盖基础环境搭建、核心特性(如密封类、虚拟线程)、模块化开发、响应式编程、主流框架(Spring Boot 3、Spring Security 6)、数据库操作(JPA + Hibernate 6)及微服务实战,助你掌握企业级开发技能。
248 3
|
1月前
|
算法 Java 测试技术
零基础学 Java: 从语法入门到企业级项目实战的详细学习路线解析
本文为零基础学习者提供完整的Java学习路线,涵盖语法基础、面向对象编程、数据结构与算法、多线程、JVM原理、Spring框架、Spring Boot及项目实战,助你从入门到进阶,系统掌握Java编程技能,提升实战开发能力。
109 0
|
1月前
|
前端开发 Java 数据库
Java 项目实战从入门到精通 :Java Web 在线商城项目开发指南
本文介绍了一个基于Java Web的在线商城项目,涵盖技术方案与应用实例。项目采用Spring、Spring MVC和MyBatis框架,结合MySQL数据库,实现商品展示、购物车、用户注册登录等核心功能。通过Spring Boot快速搭建项目结构,使用JPA进行数据持久化,并通过Thymeleaf模板展示页面。项目结构清晰,适合Java Web初学者学习与拓展。
178 1
|
2月前
|
存储 缓存 NoSQL
java 集合入门基础理论的核心概念与实用长尾知识
本文介绍了Java集合框架的基础理论知识,包括单列集合(List、Set、Queue)和双列集合(Map)的特点及常用实现类(如ArrayList、HashSet、HashMap等)。详细讲解了集合的遍历方式(迭代器、增强for循环、Lambda表达式)和典型应用场景(如数据去重、键值存储等)。通过具体代码示例,帮助初学者理解集合框架的核心概念和实际应用,为Java编程中的数据存储与管理提供基础指导。
91 0