Spring Boot 可执行 jar 可执行 war 结构

原文地址 https://docs.spring.io/spring-boot/docs/1.5.19.BUILD-SNAPSHOT/reference/htmlsingle/#executable-jar

Spring引导加载程序模块允许Spring引导支持可执行jar和war文件。如果您正在使用Maven或Gradle插件,那么可执行jar将自动生成,您通常不需要知道它们如何工作的细节。

如果您需要从不同的构建系统创建可执行jar,或者您只是对底层技术感兴趣,那么本节将提供一些背景知识。

E.1嵌套的jar

Java没有提供任何加载嵌套的jar文件(即包含在jar中的jar文件)的标准方法。如果您希望分发一个自包含的应用程序,您可以只从命令行运行该应用程序,而不需要解压缩,那么这可能会有问题。
为了解决这个问题,许多开发人员使用“shaded”jar。shaded jar只是将所有jar中的所有类打包到一个“uber jar”中。shaded jar的问题是,很难看到在应用程序中实际使用的库。如果在多个jar中使用相同的文件名(但内容不同),也会出现问题。Spring Boot采用了一种不同的方法,允许您直接嵌套jar。

E.1.1 可执行jar文件结构

Spring引导加载程序兼容的jar文件的结构应该如下所示:

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

应用程序类应该放在一个嵌套的BOOT-INF/classes目录中. 依赖项应该放在一个嵌套的BOOT-INF/lib目录中。

E.1.2 可执行的war文件结构

兼容Spring Boot Loader的war文件的结构应该是这样的:

example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar

依赖项应该放在嵌套的WEB-INF/lib目录中。嵌入式运行时需要但部署到传统web容器时不需要的任何依赖项都应该放在WEB-INF/lib-provide中。

E.2 Spring Boot’s “JarFile” class

用于支持加载嵌套jar的核心类是org.springframework.boot.loader.jar.JarFile。它允许您加载标准jar文件或嵌套的jar文件。第一次加载时,每个JarEntry的位置都被映射成外部jar的文件偏移量

myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
||     A.class      |||  B.class  |  C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
 ^                    ^           ^
 0063                 3452        3980

上面的例子展示了A。类可以在myapp.jar中的/BOOT-INF/classes中找到相当于myapp.jar的0063文件偏移量的位置。B class 可以在myapp嵌套jar的3452文件偏移量中找到,C class 可以再3980位置找到。

有了这些信息,我们可以通过查找外部jar的适当部分来加载特定的嵌套jar。我们不需要解压归档文件,也不需要将所有条目数据读入内存。

E.2.1 与标准Java“JarFile”的兼容性

Spring引导加载程序努力保持与现有代码和库的兼容性。org.springframework.boot.loader.jar.JarFilejava.util.jar.JarFile的扩展。是可替换的。getURL()方法将返回一个URL,该URL打开一个兼容java.net.JarURLConnection的连接,可以与Java的URLClassLoader一起使用。

E.3 启动可执行jar

org.springframework.boot.loader.Launcher 启动器类是一个特殊的引导类,它用作一个可执行jar主入口点。它是jar文件中实际的主类,用于设置适当的URLClassLoader并最终调用main()方法。

有3个启动器子类(JarLauncher, WarLauncherPropertiesLauncher)。它们的目的是加载资源(.class文件等)从目录中的嵌套jar文件或war文件(与类路径中的显式文件相反)获取。在JarLauncherWarLauncher的情况下,嵌套路径是固定的。JarLauncherBOOT-INF/lib/ ,WarLauncherWEB-INF/lib/和WEB-INF/lib-provide/ 中查找,所以如果您想要更多,只需在这些位置添加额外的jar即可。默认情况下,PropertiesLauncher会出现在应用程序存档中的BOOT-INF/lib/中,您可以通过设置环境变量LOADER_PATHloader来添加其他位置。

E.3.1 启动程序清单(Launcher manifest)

您需要指定一个适当的启动程序作为META-INF/MANIFEST.MF的主类属性。应该在start类属性中指定要启动的实际类(即您编写的包含main方法的类)。

例如,下面是一个典型的清单。MF为可执行jar文件:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

对于war文件,它应该是:

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication

您不需要在清单文件中指定类路径项,程序会自动查找路径。

E.3.2 Exploded archives(归档文件)

某些PaaS实现可能选择在运行前解包存档。例如,你可以运行一个解压缩档案,只需启动适当的启动器:

$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher

E.4 PropertiesLauncher特性

PropertiesLauncher has a few special features that can be enabled with external properties (System properties, environment variables, manifest entries or loader.properties).

PropertiesLauncher支持从loader.properties以及(由于历史原因)application.properties加载属性。我们建议使用loader.properties,作为对应用程序的支持。application.properties已被弃用,将来可能会被删除。

Key Purpose
loader.path 逗号分隔的类路径,例如lib${HOME}/app/lib,类似 javac -classpath
loader.home 用于解析loader.path中的相对路径。如装载机。loader.path=lib ${loader.home}/lib是一个类路径位置(以及该目录中的所有jar文件)。也用于定位加载loader.properties。示例/opt/app(默认为${user.dir})。
loader.args 主方法的默认参数(以空格分隔)
loader.main 要启动的主类的名称,例如com.app.Application
loader.config.name 属性文件的名称,例如启动器(默认为加载器)。
loader.config.location 属性文件的路径,例如类路径:loader。属性(默认为loader.properties)。
loader.system 布尔标志,指示应将所有属性添加到系统属性中(默认为false)

当指定为环境变量或清单项时,应使用以下名称:

Key Manifest entry Environment variable
loader.path Loader-Path LOADER_PATH
loader.home Loader-Home LOADER_HOME
loader.args Loader-Args LOADER_ARGS
loader.main Start-Class LOADER_MAIN
loader.config.location Loader-Config-Location LOADER_CONFIG_LOCATION
loader.system Loader-System LOADER_SYSTEM

构建插件在构建fat jar时自动将Main-Class属性移动到Start-Class。如果您正在使用它,请使用Main-Class属性指定要启动的类的名称,并省略Start-Class

loader.properties 先在 loader.home查找 然后在classpath根目录查找, 最后是classpath:/BOOT-INF/classes 使用存在的第一个位置。
loader.home 只是一个附加属性文件的目录位置(覆盖默认值),只要load.config存在。没有指定位置。
loader.path 可以包含目录(递归扫描jar和zip文件)、归档路径、归档文件中扫描jar文件的目录(例如,“dependensis .jar!/lib”)或通配符模式(默认JVM行为)。存档路径可以相对于加载器。home,或者文件系统中具有jar:file:前缀的任何地方。
loader.path path(如果为空)默认为BOOT-INF/lib(意味着从存档文件运行的本地目录或嵌套目录)。由于这个属性,当没有提供额外配置时,启动器的行为与JarLauncher相同。
loader.path 不能用于配置加载程序的位置。属性(用于搜索后者的类路径是启动PropertiesLauncher时的JVM类路径)。
占位符替换是在使用前从系统和环境变量以及所有值上的属性文件本身进行的。
属性的搜索顺序是env vars、系统属性和loader(在多个位置查找是有意义的)。属性,分解的存档清单,存档清单。

E.5 可执行jar的限制

在使用Spring引导加载程序打包的应用程序时,需要考虑许多限制。

E.5.1 Zip实体压缩(Zip entry compression)

嵌套实体 ZipEntry 必须使用 ZipEntry.STORED 方法,这是必需的,以便我们可以直接查找嵌套jar中的各个内容。嵌套jar文件本身的内容仍然可以压缩,外部jar中的其他entries也可以压缩。

E.5.2 System ClassLoader

启动的应用程序在加载类时应该使用Thread.getContextClassLoader()(默认情况下,大多数库和框架都会这样做)。通过ClassLoader.getSystemClassLoader()加载嵌套的jar类将会失败。请注意java.util.Logging总是使用系统类加载器,因此您应该考虑不同的日志实现。

E.6 可选的fat jar 替代方案

如果上述限制意味着您不能使用Spring引导加载程序,可以考虑以下替代方案:

  • Maven Shade Plugin
  • JarClassLoader
  • OneJar
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容