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


    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

  • 相关阅读:
    ActivityLifecycleCallbacks 如何控制activity的生命周期
    ViewPage + Fragment 防止Fragment 重复加载问题
    RecyclerView 必知必会(转)
    如何简单的实现一个tab页title的动画效果
    onInterceptTouchEvent和onTouchEvent调用时序(转)
    Android Studio 2.3版本 Run项目不能自动启动APP的问题 (转)
    js-jQuery性能优化(一)
    js-权威指南学习笔记20
    js-权威指南学习笔记19.2
    js-权威指南学习笔记19
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/executable-jar.html
Copyright © 2020-2023  润新知