Maven打包常用插件介绍与问题分析

news2024/9/21 11:11:57

文章目录

  • 简介
  • 创建测试项目
  • maven-jar-plugin
    • 打可执行包
    • 依赖在哪里?
  • maven-assembly-plugin
  • maven-shade-plugin
  • spring-boot-maven-plugin
  • mvn打包一个比较坑的问题
  • 打包问题排查

简介

很多时候我们不太会关心maven是如何打包的,因为maven的确做得很棒,提供了很多默认的设置。

就算需要一些不同的定制,基本上也能找到插件拷贝一下就好了。

但是,当打包遇到一些比较特殊的情况,或者要分析一下jar包,我们就需要对Maven打包有多一点的了解。

本文将介绍Maven常见的打包插件,及其打包的情况,相信能帮你多了解一点Maven的打包操作。

先做一个简单的小结:

  1. maven-jar-plugin:默认使用,打依赖包啥都不用配置,打可执行包要配置main-class,它只能打依赖和可执行分离包
  2. maven-assembly-plugin、maven-shade-plugin:若果希望把依赖也打到可执行包中,可以使用这2个插件
  3. spring-boot-maven-plugin:SpringBoot打可执行jar、war包

创建测试项目

创建一个maven项目,下面是pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>vip.meet</groupId>
    <artifactId>maven-package-learn</artifactId>
    <version>1.0.0</version>
    <name>maven-package-learn</name>
    <description>maven打包</description>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>3.1.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

创建一个可执行的main类:

package vip.meet.start;

public class Main {

    public static void main(String[] args) throws Exception {
        System.out.println("Hello World!");
    }
}

maven-jar-plugin

maven-jar-plugin是Maven打包默认使用的插件,如果pom中啥都不配置,默认使用的就是该插件。

执行下面命令打包:

# clean表示先清除,package阶段会自动包含执行clean这个phase,所以不加也没有问题
mvn clean package

默认打出的包
我们可以看到打出的包很小,看一下包内情况:
无lib
可以看到没有依赖包。

META-INF\MANIFEST.MF文件中也没有主类Main-Class属性:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.8.2
Built-By: tim
Build-Jdk: 21

java -jar命令执行jar文件时,Java运行时会读取MANIFEST.MF文件,找到Main-Class属性指定的类,并执行该类的 main() 方法

没有Main-Class属性,说明默认打出来的包,不是可运行的jar包,而是依赖包。

打可执行包

那如何打出可执行包呢?

配置一下manifest的mainClass属性即可。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.3.0</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>vip.meet.start.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

这样META-INF\MANIFEST.MF文件中就有Main-Class了:

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 21
Main-Class: vip.meet.start.Main

这样我们执行下面的命令,就可以打印出我们的Hello World!了

java -jar maven-package-learn-1.0.0.jar

执行

如果觉得默认的名字不是自己想要的,可以在build下的finalName指定名字,注意不是在插件中,也不需要加.jar后缀。

<build>
    <finalName>custom-name</finalName>
</build>

这样就可以打出custom-name.jar包了。

依赖在哪里?

现在就完事大吉了吗?

当然不是!

现在还不能打依赖包。不信?

加入一个依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

改一下Main类:

package vip.meet.start;

import org.apache.commons.lang3.StringUtils;

public class Main {

    public static void main(String[] args) {
        System.out.println(StringUtils.isNotEmpty("Hello"));
        System.out.println("Hello World!");
    }
}

无依赖执行失败
这是因为默认不会打依赖包,所以运行时找不到commons-lang3这个包,自然找不到StringUtils类。

怎么办?改配置:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.3.0</version>
    <configuration>
        <archive>
        <!-- 也可以在这里指定打出包的名称,会覆盖build中的设置 -->
         <!-- <finalName>${project.artifactId}-${project.version}-mjp</finalName> -->
            <manifest>
                <mainClass>vip.meet.start.Main</mainClass>
                <!-- MANIFEST.MF添加依赖-->
                <addClasspath>true</addClasspath>
                <!-- MANIFEST.MF指定依赖路径-->
                <classpathPrefix>../lib/</classpathPrefix>
            </manifest>
        </archive>
    </configuration>
</plugin>

上面的配置是把依赖信息写到META-INF\MANIFEST.MF文件中。

够了吗?

不够,因为jar包中还是没有依赖jar包,所以还得配置一下拷贝资源插件。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

问题解决了吗?

解决了一半!因为依赖包并没有打包到jar包中,打分离包模式还可以,运行也需要-cp模式

# 不能用-jar,因为-jar会导致-cp失效,而我们需要-cp指定classpath
# 所以需要手动指定要运行的主类
java -cp maven-package-learn-1.0.0.jar;./lib/* vip.meet.start.Main

# java -jar maven-package-learn-1.0.0.jar

注意:-cp依赖,Windows 使用;分割,Linux使用:分割

java cp

那如何能把依赖的jar包也打包到jar包中呢?

可以使用maven-assembly-plugin插件

maven-assembly-plugin

pom中添加plugin插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>
                            vip.meet.start.Main
                        </mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </execution>
    </executions>
</plugin>
# 打包
mvn clean package
# 执行有依赖的包
java -jar maven-package-learn-1.0.0-jar-with-dependencies.jar

assembly打的包

我们可以看到maven-assembly-plugin打出的包比较乱,是因为它把依赖的jar包解压出来添加到最终的jar包中了。

maven-shade-plugin

maven-shade-plugin和maven-assembly-plugin很像,会在maven-jar-plugin打的包上做二次打包。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>vip.meet.start.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
# 打包
mvn clean package
# 执行有依赖的包
java -jar maven-package-learn-1.0.0.jar

spring-boot-maven-plugin

spring-boot-maven-plugin主要是最对SpringBoot项目的打包插件。

添加一个SpringBoot启动类:

package vip.meet;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

pom中配置插件:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>3.1.1</version>
    <configuration>
        <mainClass>vip.meet.Application</mainClass>
        <layout>JAR</layout>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

JAR表示打成jar包,必须大写,还可以设置为WAR、ZIP

# 打包
mvn clean package
# 执行有依赖的包
java -jar maven-package-learn-1.0.0.jar

MANIFEST.MF文件内容:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.8.2
Built-By: tim
Build-Jdk: 21
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: vip.meet.Application
Spring-Boot-Version: 3.1.1
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

spring-boot-maven-plugin打的包的结构还是比较清晰的:
springboot-maven-plugin打的包

BOOT-INF下的classes中是我们自己的类
BOOT-INF下的lib中是依赖的jar包

有朋友可能就会问了,既然SpringBoot可以把依赖包直接放在lib目录下,那我们是不是也可以直接把依赖包拷贝到maven-jar-plugin打出的包中的lib目录下呢?

答案是不能,spring-boot-maven-plugin能是因为它重新写了类加载器,不信可以看的MANIFEST.MF中的main-class是Main-Class: org.springframework.boot.loader.JarLauncher。

Spring-jarloader

如果,希望maven-jar-plugin能,也需要重写类加载器。

如果能简单做到,maven-assembly-plugin和maven-shade-plugin肯定不会选择直接解压jar包的方式。

当然如果是war包可以让在WEB-INF目录下,有兴趣的朋友可以自己看一下打出的war包结构和MANIFEST.MF文件。

mvn打包一个比较坑的问题

执行下面mvn打包命令

mvn clean package

遇到一个:

Fatal error compiling: 无效的目标发行版: 21

一直以为是maven-compiler-plugin插件配置有问题,但是不管怎么试都没用。

用IDEA的maven插件compile、jar都没用问题。

使用-X参数,打印所有debug日志

mvn -X clean package

然后发现,使用的竟然是Java8,但是我java -version看明明是21啊。

然后去看了mvn脚本:

if not "%JAVA_HOME%"=="" goto OkJHome
for %%i in (java.exe) do set "JAVACMD=%%~$PATH:i"
goto checkJCmd

:OkJHome
set "JAVACMD=%JAVA_HOME%\bin\java.exe"

使用mvn脚本,它会先去检查JAVA_HOME,如果配置了JAVA_HOME,就会找%JAVA_HOME%\bin\java.exe

如果没有配置JAVA_HOME,就会直接在可执行PATH中找java.exe,如果找到,就用这个。

很不幸,虽然我配置了21的PATH,但是JAVA_HOME还是Java8的,所以mvn脚本使用了Java8当然编译不了21版本。

所以,现在没事真不用配置JAVA_HOME了,很多组件基本都不需要这个了。

打包问题排查

为了快速方便解决问题,对于大多数人第一选择当然是搜索引擎,搜索关键字,找答案。

当我们搜索不到答案,或者搜索到的都是,大佬解决了吗?兄弟你最后是咋解决得?之类,就知道是时候为社区贡献自己力量的时候到了。

但是怎么解决问题呢?

  1. 看相关打包命令脚本,看一下实际的逻辑,检查是否是版本、或者环境原因
  2. 用where、which命令看一下脚本命令是否是自己配置了多个版本混用冲突了
  3. 看日志,耐心看一点不要放过,最好输出debug的日志、看一下到底出错在哪一步
  4. 如果上面信息都没有解决,那只能去看源码了,找到对应的错误位置,去检查对应的源码,自己动手调试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1431532.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4个最佳的免费全磁盘加密程序,总有一款适合你

全磁盘加密软件加密整个驱动器,而不仅仅是几个文件或文件夹。加密计算机的驱动器可以使你的私人数据免受窥探,即使你的计算机被盗。 你也不仅仅局限于一个硬盘驱动器。闪存驱动器和外部硬盘驱动器等外部设备也可以通过磁盘加密软件进行加密。 注意:Windows和macOS都集成了…

3. 状态管理 vuex 状态管理库

目录 3.1 vuex 介绍 3.2 使用方式 3.1 vuex 介绍 vuex 是一个专为 Vue.js 应用程序开发的状态管理库 vuex 可以在多个组件之间共享数据&#xff0c;并且共享的数据是响应式的&#xff0c;即数据的变更能及时渲染到模板 vuex 采用集中式存储管理所有组件的状态 每一个 Vuex…

【Android】RxJava系列01-基本概述和基本用法

少年啊&#xff0c;要永远相信美好的事情即将发生 【Android】RxJava系列01-基本概述和基本用法 1.RxJava的概述2.RxJava的作用3.观察者和被观察者4.背压5.RxJava的基本用法步骤一&#xff0c;创建Observer&#xff08;观察者&#xff09;步骤二&#xff0c;创建Observable&…

【C++】类和对象之运算符重载(三)

前言&#xff1a;在前面我们知道在类和对象中有六个默认成员函数&#xff0c;并学习了其中三个构造函数、析构函数、拷贝构造函数&#xff0c;今天我们将进一步的学习.赋值运算符重载。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质…

UnityShader 边缘光效果

效果&#xff1a; 代码实现&#xff1a; Shader "MyShader/Sim" {Properties{_MainTex("主贴图",2D)"white"{}_MainColor("主贴图颜色",color)(1,1,1,1)_InnerSimPower("内描边强度",Range(-1.0,3.0))0.0_InnerSimColor(&…

基于springboot篮球竞赛预约平台源码和论文

随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;篮球竞赛预约平台也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变化&#xff0c;而…

day2.4

D选项是不正确的。如果一个类没有定义默认构造函数&#xff0c;但该类的所有数据成员都有默认值&#xff0c;那么编译器会自动生成一个默认构造函数。然而&#xff0c;如果类中有某些数据成员没有默认值或者需要进行特殊的初始化&#xff0c;那么就需要用户自己定义一个默认构造…

小程序<swiper/>组件详解及使用指南

目录 引言微信小程序的重要性Swiper组件的角色与功能简介Swiper组件基础Swiper组件的定义与使用场景如何在微信小程序中引入Swiper组件Swiper组件的基本结构与属性Swiper组件的高级应用自定义Swiper指示点样式实现Swiper的动态效果(如自动播放、循环播放)说明引言 微信小程序…

【云原生运维问题记录】kubesphere登录不跳转问题

文章目录 现象问题排查 结论先行&#xff1a;kubesphere-system名称空间下reids宕机重启&#xff0c;会判断是否通过registry-proxy重新拉取镜像&#xff0c;该镜像原本是通过阿里云上拉取&#xff0c;代理上没有出现超时情况&#xff0c;导致失败。解决方案&#xff1a;删除re…

EasyX图形库学习(二、文字输出)

目录 一、文字绘制函数 字体属性结构体:logfont 文字输出 outtextxy 在指定位置输出字符串。 ​编辑 但如果直接使用,可能有以下报错&#xff1a; 三种解决方案&#xff1a; 将一个int类型的分数,输出到图形界面上 如果直接使用&#xff1a; 会把score输入进去根据A…

ES集群维护笔记

集群版本说明&#xff1a;es 7.5.1 REST接口 查看所有_cat接口 curl http://127.0.0.1:9200/_cat?v查看master节点 curl http://127.0.0.1:9200/_cat/master?v查看集群健康状态 curl http://127.0.0.1:9200/_cat/health?v查看节点状态 curl http://127.0.0.1:9200/_ca…

【测试运维】web自动化全知识点笔记第1篇:什么是Web自动化测试(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论Web自动化测试相关知识。了解什么是自动化&#xff0c;理解什么是自动化测试以及为什么要使用自动化测试。具体包含&#xff1a;WebDriver的基本操作&#xff0c;WebDriver的鼠标、键盘操作&#xff0c;下拉选择框、警告…

iview DatePicker 日期选择组件在弹窗中使用transfer,导致选择日期弹窗会关闭的问题

背景&#xff1a;在弹窗里面使用日期选择组件&#xff0c;选择组件的面板被弹窗遮挡了部分&#xff0c;所以需要使用transfer属性&#xff0c;但是使用之后组件面板插入body中了&#xff0c;面板的事件会导致弹窗关闭。 解决方案&#xff1a; 添加上transfer属性和指定的date-…

STL篇三:list

文章目录 前言1.list的介绍和使用1.1 list的介绍1.2 list的使用1.3 list的迭代器的失效 2.list的模拟实现2.1 结点的封装2.2 迭代器的封装2.2.1 正向迭代器2.2.2 反向迭代器 2.3 list功能的实现2.3.1 迭代器的实例化及begin()、end() 2.3.2 构造函数2.3.3 赋值运算符重载2.3.4 …

169. Majority Element

Given an array nums of size n, return the majority element. The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. Example 1: Input: nums [3,2,3] Output: 3 Exampl…

【开源】基于JAVA+Vue+SpringBoot的河南软件客服系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理人员2.2 业务操作人员 三、系统展示四、核心代码4.1 查询客户4.2 新增客户跟进情况4.3 查询客户历史4.4 新增服务派单4.5 新增客户服务费 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的河…

学习ArtTs -- 初见ArkTs

作者&#xff1a;Uncle_Tom 原文链接&#xff1a;学习ArtTs -- 初见ArkTs-云社区-华为云 1. 前言 需要静态分析去检查一个语言&#xff0c;必须对这个语言有深刻的认识&#xff0c;才能有效的对这个语言进行有效的检查。 我常说:“作为一个程序分析员需要比一般的程序员考虑…

Springboot写一个对接钉钉机器人的小插件

钉钉机器人 有时候我门需要监控各种事件&#xff0c;需要机器人给我发给提醒 如&#xff1a;git代码交接&#xff0c;代码合并&#xff0c; 服务器异常捕获&#xff0c;。。。。 参照钉钉给我们的开发文档&#xff0c;可以发现对接起来是非常简单哈哈 这是我写的小插件以及例子…

渗透测试培训学习笔记汇总1(小迪安全)

第一天 域名 概念&#xff1a;域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的互联网上某一台计算机或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地理位置&a…

修改MFC图标

摘要&#xff1a;本文主要讲解了MFC程序窗口图标的添加、任务栏、底部托盘的图标添加&#xff0c;以及所生成的exe文件图标的添加。 ​​​​​​​1、在资源视图添加Icon资源 透明图标怎么制作&#xff1f; 1&#xff09;点击图片》右键&#xff1a;使用画图3D进行编辑 2&a…