基于maven-jar-plugin打造一款自動識別主類的maven打包插件

🧑 博主簡介:CSDN博客專家歷代文學網(PC端可以訪問:https://literature.sinhy.com/#/?__c=1000,移動端可微信小程序搜索“歷代文學”)總架構師,15年工作經驗,精通Java編程高并發設計Springboot和微服務,熟悉LinuxESXI虛擬化以及云原生Docker和K8s,熱衷于探索科技的邊界,并將理論知識轉化為實際應用。保持對新技術的好奇心,樂于分享所學,希望通過我的實踐經歷和見解,啟發他人的創新思維。在這里,我希望能與志同道合的朋友交流探討,共同進步,一起在技術的世界里不斷學習成長。
技術合作請加本人wx(注明來自csdn):foreast_sea

在這里插入圖片描述


在這里插入圖片描述

基于maven-jar-plugin打造一款自動識別主類的maven打包插件

引言

相信每個Java開發者都經歷過這樣的場景:新建一個可執行JAR項目時,總要花幾分鐘在pom.xml里翻找主類路徑,然后小心翼翼地配置到maven-jar-plugin里。更痛苦的是當項目有多個main方法時,稍不留神就會打包失敗。這種重復勞動不僅浪費時間,還容易埋下隱患。

在Java項目構建過程中,MANIFEST.MF文件中的Main-Class屬性配置是一個關鍵但容易出錯的環節。傳統方式需要在pom.xml中顯式聲明主類路徑,這不僅增加了維護成本,在大型多模塊項目中更可能因配置遺漏導致運行時異常。Spring Boot通過@SpringBootApplication注解實現自動識別主類的機制廣受好評,但在非Spring Boot項目中這種能力卻難以直接復用。

本文將深入探討如何通過開發mainclass-finder-maven-plugin自定義插件,在Maven構建體系中實現智能主類識別與自動注入,既支持常規main方法識別,也可通過指定注解靈活定位主類,最終無縫集成到標準maven-jar-plugin打包流程中。這種方案不僅能提升構建效率,更實現了構建配置的智能化演進。


一、插件開發環境準備

插件名:mainclass-finder-maven-plugin

1.1 創建Maven插件項目

首先新建一個標準的Maven項目,pom.xml需要包含以下核心依賴:

<!-- 插件核心依賴 -->
<dependencies><!-- Maven插件開發API --><dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>3.8.6</version></dependency><!-- 注解處理器 --><dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-annotations</artifactId><version>3.6.4</version><scope>provided</scope></dependency><!-- 秘密武器:Spring Boot的類掃描工具 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-loader-tools</artifactId><version>2.7.12</version></dependency>
</dependencies><!-- 插件打包配置 -->
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-plugin-plugin</artifactId><version>3.6.4</version></plugin></plugins>
</build>

關鍵依賴說明:

  • maven-plugin-api:插件開發的基礎API
  • maven-plugin-annotations:簡化開發的注解支持
  • spring-boot-loader-tools:借用Spring Boot強大的類掃描能力

二、核心代碼實現解析

2.1 Mojo類完整實現

這是我們插件的"大腦",所有魔法都發生在這里:

import java.io.File;
import java.io.IOException;import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.springframework.boot.loader.tools.MainClassFinder;
import com.sinhy.nature.utils.ObjectUtils;/*** 主類發現者插件核心實現* 在PROCESS_CLASSES階段掃描類文件,智能識別主類* * @author lilinhai* @since 2025-04-20 09:49* @version V1.0*/
@Mojo(name = "find", defaultPhase = LifecyclePhase.PROCESS_CLASSES)
public class MainClassFinderMojo extends AbstractMojo {// 類文件輸出目錄(默認target/classes)@Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true)private File classesDirectory;// Maven項目上下文@Parameter(defaultValue = "${project}", readonly = true)private MavenProject project;/*** 主類屬性名配置(可自定義)* 示例:存放到mainClassFoundByFinderPlugin屬性*/@Parameter(defaultValue = "mainClassFoundByFinderPlugin")private String mainClassAssignmentAttributeName;/*** 按注解過濾主類(可選配置)* 示例:使用SpringBoot的啟動注解*/@Parameterprivate String findByAnnotationOnMainClass;@Overridepublic void execute() throws MojoExecutionException, MojoFailureException {try {// 核心掃描邏輯String mainClass = detectMainClass();// 屬性注入和配置修改configureJarPlugin(mainClass);} catch (IOException e) {throw new MojoFailureException("主類掃描失敗", e);}}private String detectMainClass() throws IOException, MojoFailureException {String mainClass;// 根據是否配置注解決定掃描策略if (ObjectUtils.isEmpty(findByAnnotationOnMainClass)) {getLog().info("開始掃描標準main方法...");mainClass = MainClassFinder.findSingleMainClass(classesDirectory);} else {getLog().info("按注解[" + findByAnnotationOnMainClass + "]掃描主類...");mainClass = MainClassFinder.findSingleMainClass(classesDirectory, findByAnnotationOnMainClass);}if (mainClass == null) {throw new MojoFailureException("未找到符合條件的主類!檢查:"+ (findByAnnotationOnMainClass != null ? "注解@" + findByAnnotationOnMainClass : "main方法"));}return mainClass;}private void configureJarPlugin(String mainClass) throws MojoExecutionException {// 將主類路徑存入Maven屬性池project.getProperties().setProperty(mainClassAssignmentAttributeName, mainClass);getLog().info("成功識別主類:" + mainClass);// 動態修改maven-jar-plugin配置Plugin jarPlugin = project.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:maven-jar-plugin");if (jarPlugin == null) {throw new MojoExecutionException("請確保已配置maven-jar-plugin!");}// 構建或修改XML配置節點Xpp3Dom config = (Xpp3Dom) jarPlugin.getConfiguration();if (config == null) {config = new Xpp3Dom("configuration");jarPlugin.setConfiguration(config);}// 層級結構:configuration -> archive -> manifest -> mainClassXpp3Dom archiveNode = getOrCreateNode(config, "archive");Xpp3Dom manifestNode = getOrCreateNode(archiveNode, "manifest");Xpp3Dom mainClassNode = getOrCreateNode(manifestNode, "mainClass");// 智能設置值(優先保留用戶配置)if (mainClassNode.getValue() == null || mainClassNode.getValue().contains("${")) {mainClassNode.setValue(mainClass);getLog().info("已自動配置maven-jar-plugin");}}// 輔助方法:獲取或創建XML節點private Xpp3Dom getOrCreateNode(Xpp3Dom parent, String nodeName) {Xpp3Dom node = parent.getChild(nodeName);if (node == null) {node = new Xpp3Dom(nodeName);parent.addChild(node);}return node;}
}

代碼亮點解讀:

  1. 雙模式掃描:既支持傳統main方法,也支持注解標記
  2. 配置兼容:優先保留用戶自定義配置
  3. 智能提示:通過getLog()輸出構建日志
  4. 健壯性檢查:對可能缺失的插件進行預校驗

三、插件使用指南

3.1 基礎配置

在需要使用的項目中添加:

<build><plugins><!-- 我們的智能插件 --><plugin><groupId>com.sinhy</groupId><artifactId>mainclass-finder-maven-plugin</artifactId><version>2.1.0</version><executions><execution><phase>process-classes</phase><goals><goal>find</goal></goals></execution></executions></plugin><!-- 標準打包插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifest><!-- 這里使用動態注入的屬性 --><mainClass>${mainClassFoundByFinderPlugin}</mainClass></manifest></archive></configuration></plugin></plugins>
</build>

3.2 高級配置示例

當需要使用注解過濾時:

<plugin><groupId>com.sinhy</groupId><artifactId>mainclass-finder-maven-plugin</artifactId><configuration><findByAnnotationOnMainClass>org.springframework.boot.autoconfigure.SpringBootApplication</findByAnnotationOnMainClass><!-- 可選:自定義屬性名 --><mainClassAssignmentAttributeName>autoDetectedMainClass</mainClassAssignmentAttributeName></configuration><!-- ...其余配置同上... -->
</plugin>

四、工作原理深度剖析

4.1 執行時機把控

我們將插件綁定到process-classes階段(即編譯生成class文件后),這樣就能:

  1. 確保掃描到最新編譯的類
  2. 在打包前完成主類配置

4.2 主類掃描的底層邏輯

借助MainClassFinder的兩個核心方法:

// 掃描標準main方法
public static String findSingleMainClass(File directory)// 掃描帶指定注解的類
public static String findSingleMainClass(File directory, String annotationClassName)

其內部實現原理是:

  1. 遍歷目錄下的所有.class文件
  2. 使用ASM解析字節碼
  3. 檢查是否包含main方法
  4. 驗證類/方法修飾符是否符合規范
  5. 檢查注解標記(如果配置了的話)

4.3 動態配置的奧秘

我們通過讓mainclass-finder-maven-plugin插件去修改Maven項目的PropertiesPlugin配置來實現動態注入:

// 設置全局屬性
project.getProperties().setProperty("mainClassFoundByFinderPlugin", "com.example.Main");// mainclass-finder-maven-plugin插件修改maven-jar-plugin的配置后的新XML配置
<configuration><archive><manifest><mainClass>com.example.Main</mainClass></manifest></archive>
</configuration>

五、插件運行測試效果

如下圖所示,是ddns應用項目成功的實踐。圖中顯示已通過mainclass-finder-maven-plugin插件成功自動獲取到項目的主類,且成功注入到jar包中的MANIFEST.MF文件中。
在這里插入圖片描述

總結

經過這個插件的開發實踐,我們不僅解決了具體的工程問題,更重要的是體會到了Maven插件生態的強大之處。當發現重復的配置工作時,不妨停下來想想:能不能通過自動化手段解決?

這個插件現在已經在我的團隊內部使用了半年多,累計節省了數百小時的配置時間。希望它也能給你的項目帶來便利,更期待你能在此基礎上擴展出更強大的功能!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902186.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902186.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902186.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多態的主要好處與不足

多態是面向對象編程的核心特性之一&#xff0c;它通過方法重寫、接口實現等方式實現“同一操作作用于不同對象時產生不同行為”。以下是多態的主要好處與不足&#xff1a; 多態的好處 1. 提高代碼靈活性和擴展性 開閉原則支持&#xff1a;新增子類時&#xff0c;無需修改現有…

excel解析圖片pdf附件不怕

背景 工作中肯定會有導入excel還附帶圖片附件的下面是我解析的excel&#xff0c;支持圖片、pdf、壓縮文件實現 依次去解析excel&#xff0c;看看也沒有附件&#xff0c;返回的格式是Map&#xff0c;key是第幾行&#xff0c;value是附件list附件格式都被解析成pdf格式Reader.jav…

python爬蟲 線程,進程,協程

0x00 線程 線程是一個執行單位&#xff0c;是在一個進程里面的&#xff0c;是共享進程里面的提供的內存等資源&#xff0c;使用多個線程時和使用多個進程相比&#xff0c;多個線程使用的內存等資源較少。進程像一座“房子”&#xff08;獨立資源&#xff09;&#xff0c;線程是…

ES|QL,知道嗎,專為搜索而生 —— 推出評分和語義搜索

作者&#xff1a;來自 Elastic Ioana Tagirta 在 Elasticsearch 8.18 和 9.0 中&#xff0c;ES|QL 支持評分、語義搜索以及更多的 match 函數配置選項&#xff0c;還有一個新的 KQL 函數。 使用 ES|QL 搜索 在 Elasticsearch 8.18 和 9.0 中&#xff0c;ES|QL 增加了一系列新功…

MIT6.S081-lab4

MIT6.S081-lab4 注&#xff1a;本篇lab的前置知識在《MIT6.S081-lab3前置》 1. RISC-V assembly 第一個問題 Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf? 我們先來看看main干了什么&#xff1a; …

一文總結通信電路中LC諧振回路中各公式以及對深入解讀品質因數Q

目錄 前言 一、基本公式總結 1.并聯諧振回路 2.串聯諧振回路 二、淺談品質因數 1.衡量諧振回路能量存儲與能量損耗之比的無量綱參數&#xff0c;用于描述諧振電路的頻率選擇性 2.當受到振蕩驅動力時&#xff0c;諧振腔的中心頻率與其帶寬的比值 3.為什么諧振時電容上的…

Linux:文件系統

一.認識硬件–磁盤 1. 物理結構 1.2 存儲結構 ?如何定位?個扇區呢&#xff1f; 可以先定位磁頭&#xff08;header&#xff09;——》確定磁頭要訪問哪?個柱?(磁道)&#xff08;cylinder&#xff09;——》 定位?個扇區(sector)。 柱?&#xff08;cylinder&#xff09…

數字孿生廢氣處理工藝流程

圖撲數字孿生廢氣處理工藝流程系統。通過精準 3D 建模&#xff0c;對廢氣收集、預處理、凈化、排放等全流程進行 1:1 數字化復刻&#xff0c;實時呈現設備運行參數、污染物濃度變化等關鍵數據。 借助圖撲可視化界面&#xff0c;管理者可直觀掌握廢氣處理各環節狀態&#xff0c…

Scratch——第18課 列表接龍問題

在四級的考級中&#xff0c;接龍的題目雖然在CIE中只出現過兩次&#xff0c;但是這類題目對字符串的知識點考察相對全面。 一、接龍游戲的判斷方法 接龍的內容對應的字符數 ? 已接龍內容的字符數 滿足條件>接龍內容的第一個字符數 ? 上一項接龍的最后一個字符 滿足條件…

webgl入門實例-向量在圖形學中的核心作用

在圖形學中&#xff0c;向量是描述幾何、光照、運動等核心概念的基礎工具。以下是向量在圖形學中的關鍵應用和深入解析&#xff1a; 1. 向量的核心作用 幾何表示&#xff1a;描述點、方向、法線、切線等。空間變換&#xff1a;平移、旋轉、縮放等操作依賴向量運算。光照計算&a…

Redis 是如何保證線程安全的?

Redis 是如何保證線程安全的&#xff1f; Redis 是一個高性能的鍵值數據庫&#xff0c;廣泛應用于緩存、消息隊列、實時分析等場景。由于其性能優勢&#xff0c;Redis 已經成為許多系統的核心組件之一。然而&#xff0c;很多開發者在使用 Redis 時&#xff0c;常常會問&#x…

Img2img-turbo 在2080Ti上的測試筆記

1. 介紹 [img2img-turbo]是[pytorch-CycleGAN-and-pix2pix]推薦的更新的圖像變換的代碼實現&#xff1b; 2. 配置信息 Conda環境名稱&#xff1a;img2img-turbo 3. 問題描述 當前在我們嘗試使用了官方推薦的訓練命令在2080Ti上進行訓練&#xff0c; 3.1 出現了 CUDA out …

代碼隨想錄算法訓練營第三十五天|416. 分割等和子集、698.劃分為k個相等的子集、473.火柴拼正方形

今日題目 416. 分割等和子集 題目鏈接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 思考&#xff1a;本題要將數組分為兩個子數組&#xff0c;且兩個子數組和相等&#xff0c;因此首先可以想到的條件就是數組可分為兩個&#xff0c;這要求數組元素數…

純CSS實現自動滾動到底部

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>自動滾動到底部</title><style>*…

【新人系列】Golang 入門(十五):類型斷言

? 個人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 專欄地址&#xff1a;https://blog.csdn.net/newin2020/category_12898955.html &#x1f4e3; 專欄定位&#xff1a;為 0 基礎剛入門 Golang 的小伙伴提供詳細的講解&#xff0c;也歡迎大佬們…

AI大模型發展現狀與MCP協議誕生的技術演進

1. 大模型能力邊界與用戶痛點&#xff08;2023年&#xff09; 代表模型&#xff1a;GPT-4&#xff08;OpenAI&#xff09;、Claude 3&#xff08;Anthropic&#xff09;、通義千問&#xff08;阿里云&#xff09;等展現出強大的生成能力&#xff0c;但存在明顯局限&#xff1a…

深入理解Linux中的線程控制:多線程編程的實戰技巧

個人主頁&#xff1a;chian-ocean 文章專欄-Linux 前言&#xff1a; POSIX線程&#xff08;Pthreads&#xff09; 是一種在 POSIX 標準下定義的線程庫&#xff0c;它為多線程編程提供了統一的接口&#xff0c;主要用于 UNIX 和類 UNIX 系統&#xff08;如 Linux、MacOS 和 BS…

(mac)Grafana監控系統之監控Linux的Redis

Grafana安裝-CSDN博客 普羅米修斯Prometheus監控安裝&#xff08;mac&#xff09;-CSDN博客 1.Redis_exporter安裝 直接下載 wget https://github.com/oliver006/redis_exporter/releases/download/v1.0.3/redis_exporter-v1.0.3.linux-amd64.tar.gz 解壓 tar -xvf redis_…

鴻蒙應用元服務開發-Account Kit未成年人模式訂閱和處理用戶信息變更

一、概述 通過訂閱用戶信息變更&#xff0c;您可以接收有關用戶及其賬戶的重要更新。當用戶取消元服務的授權信息、注銷華為賬號時&#xff0c;華為賬號服務器會發送通知到元服務&#xff0c;元服務可以根據通知消息進行自身業務處理。 二、用戶信息變更事件介紹 三、訂閱用…

buildroot構建根文件系統報錯(已解決大部分問題)

title: buildroot構建根文件系統報錯(set FORCE_UNSAFE_CONFIGURE1) author: cbus categories: 小知識 tags:小知識 abbrlink: 53691 date: 2025-04-20 08:03:00 錯誤1 set FORCE_UNSAFE_CONFIGURE1 在使用buildroot構建根文件系統時&#xff0c;一切按照文檔的配置&#xff0…