Java 代碼檢查工具之PMD入門使用詳細教程

介紹

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: 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) {
 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>

到此這篇關於Java 代碼檢查工具之PMD入門使用詳細教程的文章就介紹到這瞭,更多相關Java 代碼檢查工具PMD內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: