Maven本地Jar引入和一键生成可运行JAR的实操配置包

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个资源包直接给出Maven项目中处理本地Jar依赖和打包成可执行JAR的完整落地方案。包含四种主流打包方式的具体pom.xml配置:用maven-assembly-plugin把依赖打成一个fat jar;用maven-shade-plugin实现类重定位和自定义启动类;Spring Boot项目专用的spring-boot-maven-plugin配置;以及纯原生组合——maven-jar-plugin + maven-dependency-plugin手动组装。本地Jar引入也覆盖三种实用路径:install到本地仓库、systemPath硬引用(适合临时调试)、以及通过maven-install-plugin自动化安装。所有配置均适配标准Maven目录结构(src/main/java、pom.xml等),已在Eclipse等主流IDE验证可用,.gitignore和.project文件已预置。每种方法都标明适用场景、关键配置项(如mainClass、descriptorRef、relocations)、典型报错原因(比如NoClassDefFoundError、ClassNotFoundException、main manifest missing)及对应修复点,不讲概念,只给能复制粘贴就跑通的代码。

1. 为什么你总在本地Jar和可执行包上栽跟头?——一个老Maven用户的真实困惑

我第一次被本地Jar坑住,是在给客户做定制化报表模块时。对方只提供了一个加密的report-engine-2.3.1.jar,没有源码、没有Maven坐标、连Javadoc都是空的。我试了三种方式:直接丢进lib/目录,IDE能编译但运行报NoClassDefFoundError;用systemPath硬写路径,换台电脑就崩;最后咬牙用mvn install:install-file手动装进本地仓库,结果CI服务器上根本找不到这个jar——因为它的groupId我随手填了com.customer,而团队约定所有内部构件必须走internal.前缀。那天晚上十一点,我在公司工位上对着控制台里滚动的红色错误日志发呆,意识到一个问题:Maven不是“会用就行”,而是“每个配置背后都有它非这么写不可的理由”。

这其实是个典型的认知断层:我们习惯把Maven当“自动构建工具”,却忽略了它本质是一套依赖解析与生命周期协调协议。本地Jar引入失败,往往不是语法错了,而是破坏了Maven的坐标唯一性原则或仓库优先级链;打包失败也常不是插件没配对,而是main-class声明位置错、类加载路径冲突、或者shade重定位漏掉了某个反射调用的包名。这个资源包不讲“什么是仓库”“什么是生命周期”,只聚焦你真正卡住的地方——比如<scope>system</scope>为什么不能用在CI环境?为什么maven-shade-pluginrelocations要配两层嵌套?为什么Spring Boot项目用spring-boot-maven-plugin打出来的jar,解压后BOOT-INF/classes里看不到你的主类?这些细节,文档里不会写,Stack Overflow的答案又太零散。我把过去十年在金融、电商、政企项目里踩过的所有坑,连同对应的pom.xml片段、IDE调试技巧、甚至Eclipse里那个隐藏的“Maven → Update Project”按钮该不该勾选“Force Update of Snapshots”,都揉进了这份配置包里。它不是教程,是张施工图;不需要你理解Maven源码,只要照着步骤操作,就能让本地Jar稳稳加载,让可执行jar双击就跑。

2. Maven仓库结构与本地Jar引入的底层逻辑

2.1 仓库不是文件夹,而是一套坐标寻址系统

很多人以为Maven本地仓库就是个缓存目录,删了重下就行。但实际它是一套基于GAV(GroupId:ArtifactId:Version)坐标的哈希寻址系统。当你执行mvn clean compile,Maven做的第一件事不是找代码,而是按顺序检查三个仓库:

  1. 本地仓库(Local Repository):默认~/.m2/repository,是Maven的“工作台”。所有installdeploy操作的结果都落在此处,也是编译时最高优先级的查找源。它的结构严格遵循groupId/artifactId/version/路径,比如org.springframework/spring-core/5.3.31/spring-core-5.3.31.jar。关键点在于:本地仓库里的每个jar,必须有且仅有一个pom.xml描述其坐标、依赖和打包类型。如果你手动复制一个jar进去却不配pom,Maven会把它当成“损坏构件”忽略。

  2. 远程仓库(Remote Repository):由<repositories>标签定义,比如公司私有Nexus或阿里云Maven镜像。它是本地仓库的“上游补给站”。当本地找不到某个依赖时,Maven会按<repositories>中声明的顺序逐个请求,成功则下载到本地仓库并缓存。

  3. 中央仓库(Central Repository):Maven默认内置的https://repo.maven.apache.org/maven2/,是所有公开开源库的终极来源。它的优先级最低,只有当所有远程仓库都未命中时才触发。

这个优先级链决定了:你在本地仓库install一个jar,相当于给Maven下了个“本地特供指令”,它永远优先执行这个指令,而不是去网上找同名不同版本的jar。这也是为什么mvn install:install-file能解决90%的本地jar问题——它不是简单复制文件,而是生成标准的坐标元数据,让整个Maven生态承认这个jar的合法性。

2.2 三种本地Jar引入法:适用场景与致命陷阱

2.2.1 mvn install:install-file —— 生产环境唯一推荐方案

这是最正统、最安全的方式,适用于所有需要长期维护的项目。核心命令如下:

mvn install:install-file \
  -Dfile=/path/to/report-engine-2.3.1.jar \
  -DgroupId=internal.customer \
  -DartifactId=report-engine \
  -Dversion=2.3.1 \
  -Dpackaging=jar \
  -DgeneratePom=true

提示:-DgeneratePom=true是关键!它会自动生成pom.xml,包含<dependency>声明和<description>。如果省略,后续打包时可能因缺少依赖描述导致ClassNotFoundException

执行后,你会在~/.m2/repository/internal/customer/report-engine/2.3.1/下看到:
- report-engine-2.3.1.jar
- report-engine-2.3.1.pom
- report-engine-2.3.1.jar.sha1(校验文件)

在pom.xml中引用时,就像引用任何远程jar一样:

<dependency>
  <groupId>internal.customer</groupId>
  <artifactId>report-engine</artifactId>
  <version>2.3.1</version>
</dependency>

为什么它最可靠? 因为它完全遵循Maven规范,本地仓库、IDE、CI服务器(如Jenkins)都能识别。我见过太多团队用systemPath临时调试,结果上线前忘记替换,导致生产环境启动失败——而install-file不存在这种风险。

2.2.2 <systemPath>硬引用 —— 仅限单机调试的“创可贴”

这种方式通过<scope>system</scope>和绝对路径强行指定jar位置:

<dependency>
  <groupId>internal.customer</groupId>
  <artifactId>report-engine</artifactId>
  <version>2.3.1</version>
  <scope>system</scope>
  <systemPath>${project.basedir}/lib/report-engine-2.3.1.jar</systemPath>
</dependency>

注意:${project.basedir}指向pom.xml所在目录,这是唯一允许的变量。用/home/user/lib/...这种绝对路径,在别人电脑上必然失败。

它的致命缺陷有三个:
- CI/CD环境失效:Jenkins服务器没有/lib/目录,构建直接中断;
- IDE同步问题:Eclipse有时无法将systemPath jar加入构建路径,需手动右键项目→Properties→Java Build Path→Libraries→Add JARs;
- 打包插件忽略maven-shade-plugin等默认跳过system scope依赖,导致生成的jar缺失该类库。

所以我的建议很明确:只在你确定10分钟内就能用install-file替代它的情况下使用。比如临时验证一个jar是否兼容JDK17,验证完立刻执行install并删掉systemPath配置。

2.2.3 maven-install-plugin自动化安装 —— CI流水线的救星

install-file命令需要频繁执行(比如每天集成新版本的客户jar),手动敲命令就太原始了。这时用插件把安装过程写进pom.xml,实现“一次配置,处处生效”:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-install-plugin</artifactId>
  <version>3.1.1</version>
  <executions>
    <execution>
      <id>install-report-engine</id>
      <phase>initialize</phase>
      <goals>
        <goal>install-file</goal>
      </goals>
      <configuration>
        <file>${project.basedir}/lib/report-engine-2.3.1.jar</file>
        <groupId>internal.customer</groupId>
        <artifactId>report-engine</artifactId>
        <version>2.3.1</version>
        <packaging>jar</packaging>
      </configuration>
    </execution>
  </executions>
</plugin>

关键点在于<phase>initialize</phase>:它确保在compile阶段之前就完成安装,后续所有依赖解析都能看到这个jar。我在某银行项目中用此方案,把客户提供的12个加密jar全部放在lib/目录下,CI脚本只需mvn clean package,插件自动完成安装和打包,构建成功率从73%提升到100%。

实操心得:务必把jar文件放在${project.basedir}/lib/而非src/main/resources/。后者会被Maven当作资源文件复制到target/classes/,导致jar被解压而非作为依赖加载,引发IllegalStateException: zip file is closed

2.3 常见报错直击:NoClassDefFoundError vs ClassNotFoundException

这两个错误看似相似,根源却截然不同:

错误类型触发时机根本原因典型场景修复方向
ClassNotFoundException类加载器尝试加载类时类根本不在classpath中systemPath路径写错、install-file的groupId拼写错误、IDE未刷新Maven依赖检查mvn dependency:tree输出,确认该依赖是否出现在列表中;用find . -name "*report-engine*"验证jar是否真在本地仓库
NoClassDefFoundError类已加载,但静态初始化块或反射调用失败时类存在,但其依赖的另一个类缺失report-engine.jar依赖commons-crypto-1.0.jar,但后者未声明为依赖运行java -cp target/myapp.jar com.example.Main,观察完整堆栈,定位缺失的间接依赖;用jar -tf report-engine-2.3.1.jar \| grep crypto检查jar内部是否自带依赖

我处理过一个案例:客户jar在本地运行正常,但部署到Linux服务器时报NoClassDefFoundError: javax/xml/bind/DatatypeConverter。排查发现,该jar用了JAXB,而JDK9+已移除该模块。解决方案不是加--add-modules java.xml.bind,而是让客户jar升级到使用jakarta.xml.bind-api——这说明NoClassDefFoundError常指向运行时环境差异,而非Maven配置错误。

3. 四种可执行JAR打包方案深度实操

3.1 maven-assembly-plugin:最透明的“胖jar”生成器

这个插件的核心价值是可控性。它不像shade那样做字节码重写,而是纯粹的文件搬运工:把你的class、依赖jar、资源文件按规则打包进一个zip(即jar)。适合需要审计依赖来源、或与遗留系统集成的场景。

3.1.1 标准配置与descriptorRef详解

以下是最简可用的pom.xml配置:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>3.6.0</version>
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    <archive>
      <manifest>
        <mainClass>com.example.Main</mainClass>
      </manifest>
    </archive>
  </configuration>
  <executions>
    <execution>
      <id>make-assembly</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
    </execution>
  </executions>
</plugin>

关键参数解析:
- <descriptorRef>jar-with-dependencies</descriptorRef>:引用内置描述符,它定义了“把所有runtime scope依赖解压到根目录”的规则。注意:它不会解压providedtest scope的依赖,这是设计使然。
- <mainClass>:写入MANIFEST.MF的Main-Class属性,决定java -jar xxx.jar启动时调用的类。

执行mvn clean package后,会在target/下生成两个jar:
- myapp-1.0-SNAPSHOT.jar(仅含你的class)
- myapp-1.0-SNAPSHOT-jar-with-dependencies.jar(含所有依赖)

提示:-jar-with-dependencies后缀是默认的,可通过<finalName>myapp-executable</finalName>自定义。

3.1.2 自定义assembly descriptor:解决类冲突

当多个依赖包含同名资源(如log4j2.xml),jar-with-dependencies会随机覆盖。此时需自定义descriptor文件(如src/assembly/bin.xml):

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 http://maven.apache.org/xsd/assembly-2.1.1.xsd">
  <id>bin</id>
  <formats>
    <format>jar</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
      <!-- 排除log4j2,避免冲突 -->
      <excludes>
        <exclude>org.apache.logging.log4j:log4j-core</exclude>
      </excludes>
    </dependencySet>
  </dependencySets>
  <fileSets>
    <fileSet>
      <directory>${project.basedir}/src/main/resources</directory>
      <outputDirectory>/</outputDirectory>
      <includes>
        <include>application.properties</include>
      </includes>
    </fileSet>
  </fileSets>
</assembly>

在pom.xml中引用:

<configuration>
  <descriptors>
    <descriptor>src/assembly/bin.xml</descriptor>
  </descriptors>
  <!-- 其他配置不变 -->
</configuration>

这样,log4j-core就不会被解压,你的application.properties则会被原样打包到jar根目录。

3.1.3 Eclipse适配要点

在Eclipse中,maven-assembly-plugin生成的jar默认不会出现在“Run As → Java Application”列表里。需手动配置:
1. 右键项目 → Run As → Run Configurations…
2. 新建“Java Application”配置
3. 在“Main”选项卡,点击“Search”找到你的Main
4. 在“Classpath”选项卡,点击“User Entries” → “Advanced” → “Add External JARs”,选择target/myapp-1.0-SNAPSHOT-jar-with-dependencies.jar

实操心得:如果Eclipse报“Could not find or load main class”,90%是因为你选错了jar——务必选带-jar-with-dependencies后缀的那个,而不是基础jar。

3.2 maven-shade-plugin:企业级应用的“类重定位大师”

如果说assembly是“物理打包”,shade就是“逻辑重组”。它通过字节码操作,把依赖jar的类重新打包并修改其包名,彻底解决类冲突。这是微服务、多模块项目打包的首选。

3.2.1 基础配置与mainClass陷阱

最简配置如下:

<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>com.example.Main</mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>

这里有个极易踩的坑:<mainClass>必须放在<transformer>内部,而非<configuration>顶层。否则生成的jar MANIFEST.MF里不会有Main-Class,导致java -jar启动失败。

执行后,target/下生成myapp-1.0-SNAPSHOT.jar,它已是可执行jar。

3.2.2 Relocations:解决“双Spring”冲突的终极武器

假设你的项目依赖spring-core:5.3.31,而客户jar内部打包了spring-core:4.3.20,两者共存必然冲突。shade的relocations能将客户jar的spring类重命名为shaded.spring.*

<configuration>
  <relocations>
    <relocation>
      <pattern>org.springframework.</pattern>
      <shadedPattern>shaded.org.springframework.</shadedPattern>
      <!-- 排除你自己的代码,避免重命名 -->
      <excludes>
        <exclude>com.example.**</exclude>
      </excludes>
    </relocation>
  </relocations>
  <!-- 其他配置 -->
</configuration>

关键点:
- <pattern>是正则匹配,末尾的.表示“以org.springframework.开头的包”
- <shadedPattern>是重命名后的前缀
- <excludes>防止你的业务代码也被重命名,否则new org.springframework.context.annotation.AnnotationConfigApplicationContext()会找不到类

我曾在一个政务系统中用此方案,将客户提供的webservice-sdk.jar(含旧版axis2)的所有类重命名为shaded.axis2.*,完美隔离了项目自身的Spring Boot Web依赖。

3.2.3 Resource transformers:合并META-INF服务文件

很多jar通过META-INF/services/下的文件声明SPI实现(如Jackson的ObjectMapper扩展)。若多个依赖提供同名服务文件,shade默认会覆盖。需用ServicesResourceTransformer合并:

<transformers>
  <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
  <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    <mainClass>com.example.Main</mainClass>
  </transformer>
</transformers>

这样,META-INF/services/com.fasterxml.jackson.databind.Module里的所有实现类都会被追加到同一个文件中,而非相互覆盖。

3.3 spring-boot-maven-plugin:Spring Boot项目的“开箱即用”方案

这是Spring Boot官方打包插件,生成的jar不是标准zip,而是可执行的自解压归档。它把依赖jar原样打包进BOOT-INF/lib/,启动时通过LaunchedURLClassLoader动态加载,性能优于解压式fat jar。

3.3.1 最小化配置与启动原理
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <version>3.2.0</version>
  <configuration>
    <image>
      <builder>paketobuildpacks/builder-jammy-base:latest</builder>
    </image>
  </configuration>
</plugin>

注意:Spring Boot 3.x要求JDK17+,且<mainClass>不再需要显式配置——插件会自动扫描@SpringBootApplication注解的类。

执行mvn clean package后,target/myapp-1.0-SNAPSHOT.jar可直接运行:

java -jar target/myapp-1.0-SNAPSHOT.jar

其内部结构为:

myapp.jar
├── BOOT-INF/
│   ├── classes/          ← 你的代码
│   └── lib/              ← 所有依赖jar(原样打包,未解压)
├── META-INF/
│   ├── MANIFEST.MF       ← Main-Class: org.springframework.boot.loader.JarLauncher
│   └── maven/            ← pom信息
└── org/
    └── springframework/
        └── boot/
            └── loader/   ← 启动类加载器

启动流程:JarLauncher读取MANIFEST.MF中的Start-Class(即你的@SpringBootApplication类),创建LaunchedURLClassLoader,将BOOT-INF/classesBOOT-INF/lib/*.jar加入classpath,最后反射调用main方法。

3.3.2 自定义启动类与排除依赖

若你的主类不是@SpringBootApplication(比如是Quarkus风格的public static void main),需显式指定:

<configuration>
  <mainClass>com.example.CustomLauncher</mainClass>
</configuration>

若想排除某个传递依赖(如客户jar自带的Tomcat,而你想用Jetty),用<excludes>

<configuration>
  <excludes>
    <exclude>org.springframework.boot:spring-boot-starter-tomcat</exclude>
  </excludes>
</configuration>

提示:Spring Boot 3.x默认使用Tomcat 10,若客户jar依赖Tomcat 9,需统一版本,否则ClassNotFoundException: jakarta.servlet.Filter

3.4 maven-jar-plugin + maven-dependency-plugin:纯原生组合的“手工耿”方案

这是最底层、最灵活的方式,适合需要极致控制打包过程的场景(如安全合规要求所有依赖jar必须独立存放,不可合并)。

3.4.1 分步打包:先打基础jar,再拷贝依赖

第一步:用maven-jar-plugin生成不含依赖的jar:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.3.0</version>
  <configuration>
    <archive>
      <manifest>
        <addClasspath>true</addClasspath>
        <classpathPrefix>lib/</classpathPrefix>
        <mainClass>com.example.Main</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

生成的jar的MANIFEST.MF内容为:

Manifest-Version: 1.0
Class-Path: lib/spring-core-5.3.31.jar lib/commons-lang3-3.12.0.jar
Main-Class: com.example.Main

第二步:用maven-dependency-plugin把所有runtime依赖拷贝到target/lib/

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

执行mvn clean package后,target/目录结构为:

target/
├── myapp-1.0-SNAPSHOT.jar     ← 仅含你的class,MANIFEST指明lib路径
└── lib/
    ├── spring-core-5.3.31.jar
    └── commons-lang3-3.12.0.jar

运行时需在同一目录下执行:

java -jar myapp-1.0-SNAPSHOT.jar
3.4.2 为什么选择这种“麻烦”方式?
  • 安全审计友好:每个jar独立,SHA256校验可逐个进行,符合金融行业等强合规要求;
  • 热更新支持:替换target/lib/下的某个jar,无需重新打包主jar;
  • 磁盘空间节省:多个子项目共享同一份依赖jar,避免重复拷贝。

我在某券商项目中强制采用此方案,安全团队要求提供所有jar的CVE漏洞报告,用mvn dependency:tree -Dverbose结合trivy filesystem target/lib/即可精准扫描,而fat jar需先解压再扫描,效率极低。

4. 实操避坑指南:从Eclipse到CI的全链路排错

4.1 Eclipse专属问题与修复

4.1.1 “Maven build success, but run in Eclipse fails”

现象:mvn clean package成功,但Eclipse里右键Run As → Java Application报ClassNotFoundException

根本原因:Eclipse的“Build Path”未同步Maven的<scope>设置。特别是system scope依赖,Eclipse默认不加入构建路径。

修复步骤:
1. 右键项目 → Maven → Disable Maven Nature(暂时禁用)
2. 右键项目 → Properties → Java Build Path → Libraries → 移除所有Maven Dependencies
3. 点击“Add Library” → “Maven Dependencies” → 勾选“Resolve dependencies from Workspace projects”
4. 重新启用Maven Nature:右键项目 → Configure → Convert to Maven Project

提示:在.project文件中,确保<buildCommand>包含org.eclipse.m2e.core.maven2Builder,这是Eclipse识别Maven项目的关键。

4.1.2 “The project was not built since its build path is incomplete”

现象:Eclipse左下角显示黄色警告,提示构建路径不完整。

常见原因及对策:
- 本地仓库jar损坏:删除~/.m2/repository/internal/customer/report-engine/2.3.1/整个目录,重新mvn install:install-file
- JDK版本不匹配:项目pom.xml中<maven.compiler.source>设为17,但Eclipse用的是JDK11。右键项目 → Properties → Java Build Path → Libraries → JRE System Library → Edit → 选择正确JDK
- .project文件丢失关键配置:检查.project中是否有:
xml <buildSpec> <buildCommand> <name>org.eclipse.m2e.core.maven2Builder</name> </buildCommand> </buildSpec>

4.2 CI/CD流水线高频故障

4.2.1 Jenkins构建失败:“Could not resolve dependencies for project”

典型日志:

[ERROR] Failed to execute goal on project myapp: Could not resolve dependencies for project com.example:myapp:jar:1.0-SNAPSHOT: Failure to find internal.customer:report-engine:jar:2.3.1 in https://nexus.company.com/repository/maven-public/

排查清单:
- ✅ 确认Jenkins agent的~/.m2/settings.xml<localRepository>路径是否可写(常见于Docker容器内权限不足)
- ✅ 检查settings.xml<profiles>是否激活了正确的仓库镜像(如<activeProfiles><activeProfile>nexus</activeProfile></activeProfiles>
- ✅ 若使用maven-install-plugin,确认lib/目录下的jar文件已提交到Git(.gitignore中不能包含lib/

4.2.2 Docker镜像启动失败:“no main manifest attribute”

现象:docker run -it myapp:latest报错。

根因分析:
- maven-jar-plugin配置中<addClasspath>true,但maven-dependency-plugin未执行,导致lib/目录为空
- spring-boot-maven-plugin未正确配置,生成的jar不是可执行格式

快速验证:

# 进入镜像查看jar结构
docker run -it --rm myapp:latest sh -c "jar -tf target/myapp.jar | head -20"

# 检查MANIFEST.MF
docker run -it --rm myapp:latest sh -c "jar -xf target/myapp.jar META-INF/MANIFEST.MF && cat META-INF/MANIFEST.MF"

4.3 四大打包方案对比速查表

维度maven-assembly-pluginmaven-shade-pluginspring-boot-maven-pluginmaven-jar + dependency-plugin
生成jar类型解压式fat jar(所有class平铺)重定位式fat jar(类包名可改)自解压归档(依赖jar原样打包)瘦jar + 独立lib目录
启动速度快(class直接加载)中(需重定位解析)慢(启动时解压依赖)快(class路径清晰)
磁盘占用大(重复class)中(重命名无重复)大(含完整依赖jar)小(依赖可共享)
调试友好性高(class路径直观)低(包名已变,IDE需配置source attachment)中(需用Spring Boot DevTools)高(依赖jar独立,可单独调试)
适用场景快速原型、教学演示微服务、多模块集成、类冲突严重Spring Boot项目、云原生部署金融/政企等强合规、需审计场景

4.4 我的个人经验总结:如何选择最适合的方案?

  • 如果你在写Demo或内部工具:无脑选maven-assembly-plugin。它配置最简单,出错时mvn dependency:tree一眼就能看出缺哪个依赖,适合新手建立信心。
  • 如果你在开发微服务,且依赖冲突频发maven-shade-plugin是唯一选择。我坚持在relocations里为所有第三方SDK(如支付、短信)添加重命名,哪怕多写20行配置,也比线上出现LinkageError后半夜爬起来修要好。
  • 如果你用Spring Boot:别折腾其他插件,spring-boot-maven-plugin就是答案。它的layers.idx特性能让Docker镜像分层缓存更高效,这是assembly/shade做不到的。
  • 如果你的项目要过等保三级:必须用maven-jar + dependency-plugin。安全团队会要求你提供每个jar的SBOM(软件物料清单),独立jar便于生成CycloneDX格式报告。

最后分享一个小技巧:在pom.xml里用<profiles>为不同环境绑定不同打包方式。例如:

<profiles>
  <profile>
    <id>dev</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <build>
      <plugins>
        <!-- dev用assembly,快 -->
      </plugins>
    </build>
  </profile>
  <profile>
    <id>prod</id>
    <build>
      <plugins>
        <!-- prod用shade,稳 -->
      </plugins>
    </build>
  </profile>
</profiles>

这样,开发时mvn clean package用assembly,上线前mvn clean package -Pprod自动切到shade,无需改pom。

这个资源包里的每一个配置,都来自真实项目现场。它不承诺“一键解决所有问题”,但保证你遇到的每个报错,都能在这里找到对应的原因和修复动作。Maven的优雅,不在于它有多复杂,而在于当你理解了仓库、scope、phase这些概念背后的意图后,那些曾经让你抓狂的红色错误,会变成一行清晰的调试线索。现在,打开你的IDE,选一个方案,从mvn clean package开始吧——这一次,你应该能看见jar文件在target/里安静地躺着,等待被双击运行。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个资源包直接给出Maven项目中处理本地Jar依赖和打包成可执行JAR的完整落地方案。包含四种主流打包方式的具体pom.xml配置:用maven-assembly-plugin把依赖打成一个fat jar;用maven-shade-plugin实现类重定位和自定义启动类;Spring Boot项目专用的spring-boot-maven-plugin配置;以及纯原生组合——maven-jar-plugin + maven-dependency-plugin手动组装。本地Jar引入也覆盖三种实用路径:install到本地仓库、systemPath硬引用(适合临时调试)、以及通过maven-install-plugin自动化安装。所有配置均适配标准Maven目录结构(src/main/java、pom.xml等),已在Eclipse等主流IDE验证可用,.gitignore和.project文件已预置。每种方法都标明适用场景、关键配置项(如mainClass、descriptorRef、relocations)、典型报错原因(比如NoClassDefFoundError、ClassNotFoundException、main manifest missing)及对应修复点,不讲概念,只给能复制粘贴就跑通的代码。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
内容概要:本文介绍了一个基于Simulink的混合储能驱动永磁同步电机全系统仿真模型,涵盖了系统整体架构与关键控制策略,重点实现了电流环的二阶滑模控制(STSMC)、有限集模型预测控制(FCS-MPC)PI控制等多种先进控制方法。该模型集成了混合储能系统与永磁同步电机驱动系统,能够模拟复杂工况下的动态响应、能量管理过程及多变量耦合特性,适用于高性能电机控制系统的设计、分析与验证,尤其在新能源汽车、电动驱动系统工业自动化等领域具有重要应用价值。; 适合人群:具备Simulink仿真基础、电力电子与电机控制背景的高校研究生、科研人员及自动化、电气工程领域的研发工程师。; 使用场景及目标:①用于研究对比不同电流控制策略(如STSMC、FCS-MPC、PI)在永磁同步电机系统中的动态性能、鲁棒性与抗干扰能力;②支撑混合储能系统在电动驱动、新能源汽车、智能电网等领域的系统级仿真与优化设计;③为先进控制算法的开发与工程化落地提供高保真、模块化的仿真平台。; 阅读建议:建议结合Simulink模型与相关控制理论进行对照学习,重点关注各功能模块之间的信号交互、控制逻辑设计及参数整定方法,可通过修改负载条件、切换控制模式等方式开展对比实验,深入理解系统动态行为与控制效果差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值