可运行jar包的几种打包/部署方式

简介: java项目开发中,最终生成的jar,大概可分为二类,一类是一些通用的工具类(不包含main入口方法),另一类是可直接运行的jar包(有main入口方法),下面主要讲的是后者,要让一个jar文件可直接运行,通常有几下三种方式: 动工之前,先搭一个项目架子便于后面分析: 项目结构图:  这是一个gradle项目(当然,也可以换成ant项目或maven项目,这不重要),里面有二个模块,my-jar的DemoApp里提供了main入口方法,通常一个可运行的java项目,都会依赖其它一些jar包,所以另一个模块my-lib模拟工具类的jar包,即:简单来说,my-jar依赖my-lib。

java项目开发中,最终生成的jar,大概可分为二类,一类是一些通用的工具类(不包含main入口方法),另一类是可直接运行的jar包(有main入口方法),下面主要讲的是后者,要让一个jar文件可直接运行,通常有几下三种方式:

动工之前,先搭一个项目架子便于后面分析:

项目结构图:
项目结构图 

这是一个gradle项目(当然,也可以换成ant项目或maven项目,这不重要),里面有二个模块,my-jar的DemoApp里提供了main入口方法,通常一个可运行的java项目,都会依赖其它一些jar包,所以另一个模块my-lib模拟工具类的jar包,即:简单来说,my-jar依赖my-lib。

my-lib中DemoLib类的源代码:

1 package yjmyzz.runnable.lib;
2 
3 public class DemoLib {
4 
5     public static void demo() {
6         System.out.println("我是DemoLib中的demo方法");
7     }
8 }

my-jar中DemoApp类的源代码:

 1 package yjmyzz.runnable.jar;
 2 
 3 import yjmyzz.runnable.lib.DemoLib;
 4 
 5 public class DemoApp {
 6 
 7     public static void main(String[] args) {
 8         DemoLib.demo();
 9     }
10 }

二个项目编译后,会得到二个jar包:my-jar.jar及my-lib.jar

 

方法一:不借助manifest文件

java -classpath jar1:jar2:jar3...  mainClassName

解解一下:

红色的是固定部分,中间蓝色的是jar包的路径(多个jar之间用:号连接),最后绿色的部分是main方法所在的类名,按这个思路

把这二个jar包扔同一个目录下,输入如下命令:

java -classpath my-jar.jar:my-lib.jar yjmyzz.runnable.jar.DemoApp

程序就能跑起来了

 

方法二:借助manifest文件

想办法在my-jar.jar中添加MANIFEST.MF文件,内容参考下面这样:

Main-Class: yjmyzz.runnable.jar.DemoApp
Class-Path: my-lib.jar

同样,将这二个jar包扔在一起,然后

java -jar my-jar.jar 就能运行了,至于如何在打包里,自动添加MANIFEST.MF文件,gradle下可以这么做:

jar {
    manifest {
        attributes 'Main-Class': 'yjmyzz.runnable.jar.DemoApp'
        attributes 'Class-Path': 'my-lib.jar'
    }
}

 build.gradle中添加这一段即可,如果是maven项目,请参考  maven: 打包可运行的jar包(java application)及依赖项处理 一文

 

方法三:借助spring-boot 插件

前面二种方法,主程序的jar包,与依赖的jar包是分开的,这在云环境中,上传部署比较麻烦,得传多个文件(或者上传前,先压缩成一个包,再传到服务器上解压),服务器节点多时,操作起来太累。又到我大Spring出场了,将my-jar项目中的build.gradle改成下面这样:

apply plugin: 'java'
apply plugin: 'spring-boot'

buildscript {
    repositories {
        maven {
            url 'http://maven.oschina.net/content/groups/public/'
        }
    }

    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.0.RELEASE")
    }
}

repositories {
    maven {
        url 'http://maven.oschina.net/content/groups/public/'
    }
}

dependencies {
    compile project(':my-lib')
}

然后命令行输入 gradle bootRepackage 将在build/libs下生成二个文件my-jar.jar以及my-jar.jar.original(先不用管这个original文件是啥)

直接java -jar my-jar.jar 就能运行了(注意:这种方式下,并不需要my-lib.jar这类依赖的jar文件),其原理在于spring-boot插件把所有依赖的jar包,全都打到一个jar包里了。

基本上,到这里这篇文章就可以完结了,如果有同学对spring-boot这种打包方式比较好奇,想深入研究,可以继续向下看,把my-jar.jar.original改名为my-jar-origin.jar,然后输入jar tf my-jar-origin.jar 即显示这个jar包的内容,会得到以下输出:

META-INF/
META-INF/MANIFEST.MF
yjmyzz/
yjmyzz/runnable/
yjmyzz/runnable/jar/
yjmyzz/runnable/jar/DemoApp.class

即.original文件,其实就是一个普通的jar包,其中的MANIFEST.MF并没有什么实质性内容,只是一个空壳,这样的jar包,跟方法1得到的jar包是一样的。

再输入jar tf my-jar.jar,会得到以下输出:

 1 META-INF/
 2 META-INF/MANIFEST.MF
 3 yjmyzz/
 4 yjmyzz/runnable/
 5 yjmyzz/runnable/jar/
 6 yjmyzz/runnable/jar/DemoApp.class
 7 lib/
 8 lib/my-lib.jar
 9 org/
10 org/springframework/
11 org/springframework/boot/
12 org/springframework/boot/loader/
13 org/springframework/boot/loader/LaunchedURLClassLoader$Java7LockProvider.class
14 org/springframework/boot/loader/PropertiesLauncher$ArchiveEntryFilter.class
15 org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class
16 org/springframework/boot/loader/ExecutableArchiveLauncher$1.class
17 org/springframework/boot/loader/PropertiesLauncher.class
18 org/springframework/boot/loader/LaunchedURLClassLoader$ResourceEnumeration.class
19 org/springframework/boot/loader/data/
20 org/springframework/boot/loader/data/ByteArrayRandomAccessData.class
21 org/springframework/boot/loader/data/RandomAccessDataFile$DataInputStream.class
22 org/springframework/boot/loader/data/RandomAccessData.class
23 org/springframework/boot/loader/data/RandomAccessDataFile$FilePool.class
24 org/springframework/boot/loader/data/RandomAccessDataFile.class
25 org/springframework/boot/loader/data/RandomAccessData$ResourceAccess.class
26 org/springframework/boot/loader/util/
27 org/springframework/boot/loader/util/SystemPropertyUtils.class
28 org/springframework/boot/loader/util/AsciiBytes.class
29 org/springframework/boot/loader/LaunchedURLClassLoader$1.class
30 org/springframework/boot/loader/InputArgumentsJavaAgentDetector.class
31 org/springframework/boot/loader/Launcher.class
32 org/springframework/boot/loader/LaunchedURLClassLoader.class
33 org/springframework/boot/loader/JarLauncher.class
34 org/springframework/boot/loader/jar/
35 org/springframework/boot/loader/jar/JarEntryFilter.class
36 org/springframework/boot/loader/jar/JarURLConnection.class
37 org/springframework/boot/loader/jar/JarEntry.class
38 org/springframework/boot/loader/jar/Bytes.class
39 org/springframework/boot/loader/jar/CentralDirectoryEndRecord.class
40 org/springframework/boot/loader/jar/JarFile$2.class
41 org/springframework/boot/loader/jar/ZipInflaterInputStream.class
42 org/springframework/boot/loader/jar/JarFile.class
43 org/springframework/boot/loader/jar/JarFile$1.class
44 org/springframework/boot/loader/jar/JarURLConnection$1.class
45 org/springframework/boot/loader/jar/Handler.class
46 org/springframework/boot/loader/jar/JarURLConnection$JarEntryName.class
47 org/springframework/boot/loader/jar/JarEntryData.class
48 org/springframework/boot/loader/MainMethodRunner.class
49 org/springframework/boot/loader/InputArgumentsJavaAgentDetector$1.class
50 org/springframework/boot/loader/WarLauncher.class
51 org/springframework/boot/loader/PropertiesLauncher$1.class
52 org/springframework/boot/loader/ExecutableArchiveLauncher.class
53 org/springframework/boot/loader/LaunchedURLClassLoader$LockProvider.class
54 org/springframework/boot/loader/archive/
55 org/springframework/boot/loader/archive/JarFileArchive$JarFileEntry.class
56 org/springframework/boot/loader/archive/JarFileArchive.class
57 org/springframework/boot/loader/archive/FilteredArchive.class
58 org/springframework/boot/loader/archive/JarFileArchive$1.class
59 org/springframework/boot/loader/archive/ExplodedArchive.class
60 org/springframework/boot/loader/archive/FilteredArchive$2.class
61 org/springframework/boot/loader/archive/Archive$Entry.class
62 org/springframework/boot/loader/archive/ExplodedArchive$1.class
63 org/springframework/boot/loader/archive/Archive$EntryFilter.class
64 org/springframework/boot/loader/archive/FilteredArchive$1.class
65 org/springframework/boot/loader/archive/ExplodedArchive$FileEntry.class
66 org/springframework/boot/loader/archive/Archive.class
67 org/springframework/boot/loader/archive/Archive$EntryRenameFilter.class
68 org/springframework/boot/loader/archive/ExplodedArchive$FilteredURLStreamHandler.class
69 org/springframework/boot/loader/archive/ExplodedArchive$FileNotFoundURLConnection.class
70 org/springframework/boot/loader/JavaAgentDetector.class

很明显,多出了很多内容,注意第8行,可以发现my-lib.jar这个依赖的jar包,已经打包到my-jar.jar内部了,这时的MANIFEST.MF内容为:

1 Manifest-Version: 1.0
2 Start-Class: yjmyzz.runnable.jar.DemoApp
3 Spring-Boot-Version: 1.3.0.RELEASE
4 Main-Class: org.springframework.boot.loader.JarLauncher

Main-Class被设置成org.springframework.boot.loader.JarLauncher,此外还增加了Start-Class指向我们真正的程序入口yjmyzz.runnable.jar.DemoApp,换句话说,程序运行时,先调用org.springframework.boot.loader.JarLauncher,然后找到Start-Class对应的类,最终运行,运行过程中,会查找内部lib下的依赖jar包my-lib.jar,当然这一切是需要有额外的代码来处理的,所以多出来的org/springframework/boot下的一堆class,就是spring用来干这件事儿的。

第三种方式,很适合云环境的部署,只需要扔一个jar包上去就完事了,这种all-in-one的jar包,也被换为fat-jar。

 

参考文章:

http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar

目录
相关文章
|
1月前
|
前端开发 JavaScript Java
Java打包jar运行时分离lib和jar
在`pom.xml`的`build`节点中,设置`packaging`为`jar`,并配置插件分离依赖库到`lib`目录和资源文件到`resources`目录。这样可以在运行时通过`-Dloader.path=lib,resources`加载外部依赖和资源文件,便于独立升级依赖库和修改资源文件,而无需重新打包程序。具体插件包括`maven-dependency-plugin`、`maven-resources-plugin`和`spring-boot-maven-plugin`等。
72 1
|
3月前
|
Java Maven
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
在Java项目中,启动jar包时遇到“no main manifest attribute”错误,且打包大小明显偏小。常见原因包括:1) Maven配置中跳过主程序打包;2) 缺少Manifest文件或Main-Class属性。解决方案如下:
1005 8
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
|
6月前
|
Kubernetes Java Docker
利用(K8S)配合Docker部署jar包
通过Docker打包并部署到Kubernetes(K8S)集群的过程。首先,通过SpringBoot生成jar包,接着在K8S环境中创建并编辑Dockerfile文件。随后构建Docker镜像,并将其推送到镜像仓库。最后,通过一系列kubectl命令(如get pods、get svc、logs等),展示了如何在K8S中管理应用,包括查看Pod状态、服务信息、Pod日志以及重启Pod等操作。
271 3
|
7月前
|
Java 应用服务中间件 Spring
为什么SpringBoot的 jar 可以直接运行?
SpringBoot的 jar 可以直接运行的原因
529 2
|
4月前
|
Java 应用服务中间件 Maven
Maven的三种项目打包方式——pom,jar,war的区别
Maven 提供了多种打包方式,分别适用于不同类型的项目。pom 用于父项目或聚合项目,便于项目的结构和依赖管理;jar 用于Java类库或可执行的Java应用程序;war 则专用于Java Web应用程序的部署。理解这些打包方式的用途和特点,可以帮助开发者更好地配置和管理Maven项目,确保构建和部署过程的顺利进行。无论是单模块项目还是多模块项目,选择合适的打包方式对于项目的成功至关重要。
479 3
|
5月前
|
存储 数据可视化 Java
震惊!如何在linux下部署项目,部署/运行jar包 超详细保姆级教程!
如何在Linux系统下部署和运行Java项目jar包,包括传输文件到Linux、使用nohup命令运行jar包、查看端口状态、杀死进程和查看项目运行状态,以及如何解决“没有主清单属性”的错误。
993 2
震惊!如何在linux下部署项目,部署/运行jar包 超详细保姆级教程!
|
5月前
|
Java Windows
如何在windows上运行jar包/JAR文件 如何在cmd上运行 jar包 保姆级教程 超详细
本文提供了一个详细的教程,解释了如何在Windows操作系统的命令提示符(cmd)中运行JAR文件。
1881 1
|
7月前
|
Java Maven 容器
Maven使用IDEA自带工具打包,同时将lib下的jar包打入,双击jar包可直接运行
使用IntelliJ IDEA的Artifacts功能,可以将项目依赖的第三方jar包打包进jar文件中,实现双击jar包即可直接运行。
Maven使用IDEA自带工具打包,同时将lib下的jar包打入,双击jar包可直接运行
|
7月前
|
Java Maven C++
【Azure Developer】记录一次使用Java Azure Key Vault Secret示例代码生成的Jar包,单独运行出现 no main manifest attribute, in target/demo-1.0-SNAPSHOT.jar 错误消息
【Azure Developer】记录一次使用Java Azure Key Vault Secret示例代码生成的Jar包,单独运行出现 no main manifest attribute, in target/demo-1.0-SNAPSHOT.jar 错误消息
134 0
|
7月前
|
Java Linux Shell
【Azure 应用服务】部署Jar到App Service for Linux,因启动命令路径配置错误而引起:( Application Error 问题
【Azure 应用服务】部署Jar到App Service for Linux,因启动命令路径配置错误而引起:( Application Error 问题