| 本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
spring-boot-loader 模块让Spring Boot支持可执行的jar和war文件。
如果你使用Maven插件或Gradle插件,可执行的jar就会自动生成,你一般不需要了解它们的工作细节。
如果你需要从不同的构建系统中创建可执行的jar,或者你只是对底层技术感到好奇,本附录提供了一些背景。
1. 嵌套的JAR
Java没有提供任何标准的方法来加载嵌套的jar文件(也就是说,jar文件本身包含在jar中)。 如果你需要发布一个不需要解压就可以从命令行中运行的独立的应用程序,这可能是个问题。
为了解决这个问题,许多开发者使用 “shaded” jar。 一个shaded jar将所有jar中的所有类打包成一个 “uber jar”。 一个shaded jar的问题是很难看出哪些库实际上在你的应用程序中。 如果在多个jar中使用相同的文件名(但内容不同),这也会产生问题。 Spring Boot采取了不同的方法,让你直接嵌套jars。
1.1. 可执行的Jar文件结构
与Spring Boot Loader兼容的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
Application class 应放在嵌套的 BOOT-INF/classes 目录中。
依赖应该放在嵌套的 BOOT-INF/lib 目录中。
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-provided 中。
1.3. 索引文件
与Spring Boot Loader兼容的jar和war档案可以包括 BOOT-INF/ 目录下的额外索引文件。
classpath.idx 文件可以为jars和war提供,它提供了jars应该被添加到classpath的顺序。
layers.idx 文件只能用于jar,它允许将jar分割成逻辑层,用于创建 Docker/OCI 镜像。
索引文件遵循YAML兼容的语法,因此它们可以很容易地被第三方工具所解析。 然而,这些文件在内部不能被解析为YAML,它们必须完全按照下面描述的格式来写,以便使用。
1.4. Classpath 索引(Index)
classpath索引文件可以在 BOOT-INF/classpath.idx 中提供。
它提供了一个jar名称(包括目录)的列表,按照它们应该被添加到classpath中的顺序。
每一行必须以破折号空格( "-·" )开始,名称必须在双引号中。
例如,给定以下jar。
example.jar
|
+-META-INF
| +-...
+-BOOT-INF
+-classes
| +...
+-lib
+-dependency1.jar
+-dependency2.jar
索引文件将看起来像这样。
- "BOOT-INF/lib/dependency2.jar" - "BOOT-INF/lib/dependency1.jar"
1.5. 层索引(Layer Index)
图层索引文件可以在 BOOT-INF/layers.idx 中提供。
它提供了一个层的列表,以及应该包含在其中的jar的部分。
层是按照它们应该被添加到Docker/OCI镜像中的顺序写的。
层的名字写成带引号的字符串,前有破折号空格("-·"),后有冒号(":")。
图层内容是一个文件或目录名称,写成引号字符串,前缀为空格破折号("··-·")。
一个目录名以 / 结尾,一个文件名则没有。
当使用目录名时,意味着该目录内的所有文件都在同一层。
一个典型的层索引的例子是。
- "dependencies": - "BOOT-INF/lib/dependency1.jar" - "BOOT-INF/lib/dependency2.jar" - "application": - "BOOT-INF/classes/" - "META-INF/"
2. Spring Boot的 “JarFile” 类
用于支持加载嵌jar的核心类是 org.springframework.boot.loader.jar.JarFile。
它允许你从标准的jar文件或嵌套的子jar数据中加载jar内容。
当第一次加载时,每个 JarEntry 的位置被映射到外部jar的物理文件偏移量,如以下例子所示。
myapp.jar +-------------------+-------------------------+ | /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar | |+-----------------+||+-----------+----------+| || A.class ||| B.class | C.class || |+-----------------+||+-----------+----------+| +-------------------+-------------------------+ ^ ^ ^ 0063 3452 3980
前面的例子显示了在 /BOOT-INF/classes 中 myapp.jar 的 0063 位置可以找到 A.class。
嵌套jar中的 B.class 实际上可以在 myapp.jar 的 3452 位置找到,C.class 在 3980 位置。
有了这些信息,我们可以通过寻求外部jar的适当部分来加载特定的嵌套条目。 我们不需要解压存档,也不需要将所有条目数据读入内存。
3. 启动可执行的Jar
org.springframework.boot.loader.Launcher 类是一个特殊的引导类,被用作可执行jar的主入口点。
它是你的jar文件中实际的 Main-Class,它被用来设置适当的 URLClassLoader,并最终调用你的 main() 方法。
有三个启动器(Launcher)子类(JarLauncher, WarLauncher, 和 PropertiesLauncher)。
它们的目的是从嵌套的jar文件或目录中的war文件(而不是明确在classpath上的文件)加载资源(.class 文件等等)。
在 JarLauncher 和 WarLauncher 的情况下,嵌套路径是固定的。
JarLauncher 在 BOOT-INF/lib/ 中寻找,WarLauncher 在 WEB-INF/lib/ 和 WEB-INF/lib-provided/ 中寻找。
如果你想要更多,你可以在这些位置添加额外的jar。
默认情况下,PropertiesLauncher 在你的应用程序存档中寻找 BOOT-INF/lib/。
你可以通过在 loader.properties 中设置 LOADER_PATH 或 loader.path 的环境变量来添加额外的位置(这是一个逗号分隔的目录、存档或存档中的目录列表)。
3.1. Launcher 清单(Manifest)
你需要在 META-INF/MANIFEST.MF 的 Main-Class 属性中指定一个合适的 Launcher。
你想启动的实际类(也就是包含 main 方法的类)应该在 Start-Class 属性中指定。
下面的例子显示了一个典型的 MANIFEST.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
你不需要在清单文件中指定 Class-Path 条目。
classpath是由嵌套的jar推导出来的。
|
4. PropertiesLauncher 的功能
PropertiesLauncher 有一些特殊的功能,可以通过外部属性(系统属性、环境变量、清单条目(manifest)或 loader.properties)启用。
下表描述了这些属性。
| Key | 目的 |
|---|---|
|
逗号分隔的Classpath,例如 |
|
用于解决 |
|
主方法的默认参数(空格分隔)。 |
|
要启动的主类的名称(例如, |
|
属性文件(properties )的名称(例如, |
|
属性文件的路径(例如, |
|
Boolean flag,表示所有属性都应该被添加到系统属性(System properties)中。
它的默认值是 |
当指定为环境变量或清单条目时,应使用以下名称。
| Key | Manifest entry | Environment variable |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
构建插件在构建fat jar时自动将 Main-Class 属性移至 Start-Class。
如果你使用这个方法,通过使用 Main-Class 属性指定要启动的类的名称,并省略 Start-Class。
|
以下规则适用于使用 PropertiesLauncher 工作。
-
loader.properties在loader.home中搜索,然后在classpath的根部搜索,再在classpath:/BOOT-INF/classes中搜索。 第一个存在该名称的文件的位置被使用。 -
loader.home是额外的属性文件的目录位置(覆盖默认值),只有当loader.config.location没有被指定时。 -
loader.path可以包含目录(会递归扫描jar和zip文件)、归档路径、归档中扫描jar文件的目录(例如,dependencies.jar!/lib),或者通配符模式(用于JVM默认行为)。 归档路径可以是相对于loader.home的,也可以是文件系统中任何带有jar:file:前缀的地方。 -
loader.path(如果是空的)默认为BOOT-INF/lib(指的是本地目录,如果从归档文件中运行,则是嵌套目录)。 正因为如此,当没有提供额外的配置时,PropertiesLauncher的行为与JarLauncher相同。 -
loader.path不能用来配置loader.properties的位置(用于搜索后者的classpath是PropertiesLauncher启动时的JVM classpath)。 -
占位符的替换是在使用前从系统和环境变量以及属性文件本身的所有值中进行的。
-
属性的搜索顺序是环境变量、系统属性、
loader.properties、exploded archive和归档清单(archive manifest)(如果在多个地方搜索有意义)。
5. 可执行Jar的限制
在使用Spring Boot Loader打包的应用程序时,你需要考虑以下限制。
-
zip 压缩: 嵌套jar的
ZipEntry必须通过使用ZipEntry.STORED方法保存。 这是必须的,这样我们就可以直接寻求嵌套jar中的个别内容。 嵌套jar文件本身的内容仍然可以被压缩,就像外部jar中的任何其他条目一样。
-
System classLoader:启动的应用程序在加载类时应该使用
Thread.getContextClassLoader()(大多数库和框架默认都这样做)。 试图用ClassLoader.getSystemClassLoader()来加载嵌套的jar类是失败的。java.util.Logging总是使用系统类加载器。 由于这个原因,你应该考虑使用不同的日志实现。