我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring Boot如何通过java -jar启动

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring Boot如何通过java -jar启动

Pre

大家开发的基于Spring Boot 的应用 ,jar形式, 发布的时候,绝大部分都是使用java -jar 启动。 得益于Spring Boot 的封装 , 再也不用操心搭建tomcat等相关web容器le , 一切变得非常美好, 那SpringBoot是怎么做到的呢?

在这里插入图片描述

引导

新建工程 打包 启动

我们新创建一个Spring Boot的工程

在这里插入图片描述

其中打包的配置为


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

先打包一下

在这里插入图片描述

查看target目录

在这里插入图片描述

然后启动

在这里插入图片描述

在这里插入图片描述

java -jar 干啥的

我们先看看 java -jar 干了啥 ?

在oracle官网找到了该命令的描述:

If the -jar option is specified, its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its source code.

使用-jar参数时,后面的参数是的jar 【spring-0.0.1-SNAPSHOT.jar】,该jar文件中包含的是class和资源文件; 在manifest文件中有Main-Class的定义;Main-Class的源码中指定了整个应用的启动类;

简单来说: java -jar会去找jar中的manifest文件,去找到Main-Class对应的真正的启动类;

那看看去吧

在这里插入图片描述

咦 ,这个Main-Class 是Spring Boot 的。

我们还看到有个Start Class

在这里插入图片描述

官方文档中,只提到过Main-Class ,并没有提到Start-Class;
Start-Class的值是com.artisan.spring.Application,这是我们的java代码中的唯一类,包含main方法, 是能够真正的应用启动类

在这里插入图片描述

所以问题就来了:理论上看,执行java -jar命令时JarLauncher类会被执行,但实际上是com.artisan.spring.Application被执行了,这其中发生了什么呢?why?

在这里插入图片描述

打包插件

事实上,Java没有提供任何标准的方式来加载嵌套的jar文件 (jar中包含jar ,即Spring Boot 中的fat jar)

Spring Boot 默认的打包插件如下:


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

执行maven clean package之后,会生成两个文件,刚才我们也看到了

在这里插入图片描述

spring-boot-maven-plugin简介

spring-boot-maven-plugin项目存在于spring-boot-tools目录中。

spring-boot-maven-plugin默认有5个goals:repackage、run、start、stop、build-info。在打包的时候默认使用的是repackage。

spring-boot-maven-plugin的repackage能够将mvn package生成的软件包,再次打包为可执行的软件包,并将mvn package生成的软件包重命名为.original*

spring-boot-maven-plugin的repackage在代码层面调用了RepackageMojoexecute方法,而在该方法中又调用了repackage方法。


private void repackage() throws MojoExecutionException {
   // maven生成的jar,最终的命名将加上.original后缀
   Artifact source = getSourceArtifact();
   // 最终为可执行jar,即fat jar
   File target = getTargetFile();
   // 获取重新打包器,将maven生成的jar重新打包成可执行jar
   Repackager repackager = getRepackager(source.getFile());
   // 查找并过滤项目运行时依赖的jar
   Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
         getFilters(getAdditionalFilters()));
   // 将artifacts转换成libraries
   Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
         getLog());
   try {
      // 获得Spring Boot启动脚本
      LaunchScript launchScript = getLaunchScript();
      // 执行重新打包,生成fat jar
      repackager.repackage(target, libraries, launchScript);
   }catch (IOException ex) {
      throw new MojoExecutionException(ex.getMessage(), ex);
   }
   // 将maven生成的jar更新成.original文件
   updateArtifact(source, target, repackager.getBackupFile());
}

执行以上命令之后,便生成了打包结果对应的两个文件。

在这里插入图片描述

包结构

下面针对文件的内容和结构进行一探究竟。


spring-0.0.1-SNAPSHOT.jar
├── META-INF
│   └── maven(主要是pom文件)
│   └── MANIFEST.MF
├── BOOT-INF
│   ├── classes
│   │   └── 应用程序类
│   └── lib
│       └── 第三方依赖jar
└── org
    └── springframework
        └── boot
            └── loader
                └── springboot启动程序

META-INF内容


Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: spring
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.artisan.spring.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.4.1
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher
  • Main-Class:org.springframework.boot.loader.JarLauncher ,这个是jar启动的Main函数Start-Class: com.artisan.spring.Application,这个是我们应用自己的Main函数

Archive的概念

在继续了解底层概念和原理之前,我们先来了解一下Archive的概念:

  • archive即归档文件,这个概念在linux下比较常见
  • 通常就是一个tar/zip格式的压缩包
  • jar是zip格式

SpringBoot抽象了Archive的概念,一个Archive可以是jar(JarFileArchive),可以是一个文件目录(ExplodedArchive),可以抽象为统一访问资源的逻辑层

关于Spring Boot中Archive的源码如下:


public interface Archive extends Iterable<Archive.Entry> {
    // 获取该归档的url
    URL getUrl() throws MalformedURLException;
    // 获取jar!/META-INF/MANIFEST.MF或[ArchiveDir]/META-INF/MANIFEST.MF
    Manifest getManifest() throws IOException;
    // 获取jar!/BOOT-INF/lib/*.jar或[ArchiveDir]/BOOT-INF/lib/*.jar
    List<Archive> getNestedArchives(EntryFilter filter) throws IOException;
}

SpringBoot定义了一个接口用于描述资源,也就是org.springframework.boot.loader.archive.Archive

在这里插入图片描述

该接口有两个实现,分别是

  • org.springframework.boot.loader.archive.ExplodedArchive
  • org.springframework.boot.loader.archive.JarFileArchive

前者用于在文件夹目录下寻找资源,后者用于在jar包环境下寻找资源。而在SpringBoot打包的fatJar中,则是使用后者JarFileArchive

JarFile

JarFile:对jar包的封装,每个JarFileArchive都会对应一个JarFile。

JarFile被构造的时候会解析内部结构,去获取jar包里的各个文件或文件夹,这些文件或文件夹会被封装到Entry中,也存储在JarFileArchive中。如果Entry是个jar,会解析成JarFileArchive。

比如一个JarFileArchive对应的URL为:

jar:file:/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/

它对应的JarFile为:

/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar

这个JarFile有很多Entry,比如:


META-INF/
META-INF/MANIFEST.MF
spring/
spring/study/
....
spring/study/executablejar/ExecutableJarApplication.class
lib/spring-boot-starter-1.3.5.RELEASE.jar
lib/spring-boot-1.3.5.RELEASE.jar
...

JarFileArchive内部的一些依赖jar对应的URL(SpringBoot使用org.springframework.boot.loader.jar.Handler处理器来处理这些URL):


jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-starter-web-1.3.5.RELEASE.jar!/

jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.class

我们看到如果有jar包中包含jar,或者jar包中包含jar包里面的class文件,那么会使用 !/ 分隔开,这种方式只有org.springframework.boot.loader.jar.Handler能处理,它是SpringBoot内部扩展出来的一种URL协议。

在这里插入图片描述

JarLauncher工作流程

从MANIFEST.MF可以看到Main函数是JarLauncher,下面来分析它的工作流程。JarLauncher类的继承结构是:


class JarLauncher extends ExecutableArchiveLauncher
class ExecutableArchiveLauncher extends Launcher

Launcher for JAR based archives. This launcher assumes that dependency jars are included inside a /BOOT-INF/lib directory and that application classes are included inside a /BOOT-INF/classes directory.

什么意思呢?

按照定义,JarLauncher可以加载内部/BOOT-INF/lib下的jar及/BOOT-INF/classes下的应用class。


public class JarLauncher extends ExecutableArchiveLauncher {
    public JarLauncher() {}
    public static void main(String[] args) throws Exception {
        new JarLauncher().launch(args);
    }
}

其主入口新建了JarLauncher并调用父类Launcher中的launch方法启动程序。在创建JarLauncher时,父类ExecutableArchiveLauncher找到自己所在的jar,并创建archive。

JarLauncher继承于org.springframework.boot.loader.ExecutableArchiveLauncher。该类的无参构造方法最主要的功能就是构建了当前main方法所在的FatJar的JarFileArchive对象。

下面来看launch方法。该方法主要是做了2个事情:

(1)以FatJar为file作为入参,构造JarFileArchive对象。获取其中所有的资源目标,取得其Url,将这些URL作为参数,构建了一个URLClassLoader

(2)以第一步构建的ClassLoader加载MANIFEST.MF文件中Start-Class指向的业务类,并且执行静态方法main。进而启动整个程序。


public abstract class ExecutableArchiveLauncher extends Launcher {
    private final Archive archive;
    public ExecutableArchiveLauncher() {
        try {
            // 找到自己所在的jar,并创建Archive
            this.archive = createArchive();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }
}
 
 
public abstract class Launcher {
    protected final Archive createArchive() throws Exception {
        ProtectionDomain protectionDomain = getClass().getProtectionDomain();
        CodeSource codeSource = protectionDomain.getCodeSource();
        URI location = (codeSource == null ? null : codeSource.getLocation().toURI());
        String path = (location == null ? null : location.getSchemeSpecificPart());
        if (path == null) {
            throw new IllegalStateException("Unable to determine code source archive");
        }
        File root = new File(path);
        if (!root.exists()) {
            throw new IllegalStateException(
                    "Unable to determine code source archive from " + root);
        }
        return (root.isDirectory() ? new ExplodedArchive(root)
                : new JarFileArchive(root));
    }
}

在Launcher的launch方法中,通过以上archive的getNestedArchives方法找到/BOOT-INF/lib下所有jar及/BOOT-INF/classes目录所对应的archive,通过这些archives的url生成LaunchedURLClassLoader,并将其设置为线程上下文类加载器,启动应用。

至此,才执行我们应用程序主入口类的main方法,所有应用程序类文件均可通过/BOOT-INF/classes加载,所有依赖的第三方jar均可通过/BOOT-INF/lib加载。

在这里插入图片描述

小结

  • JarLauncher通过加载BOOT-INF/classes目录及BOOT-INF/lib目录下jar文件,实现了fat jar的启动。
  • SpringBoot通过扩展JarFile、JarURLConnection及URLStreamHandler,实现了jar in jar中资源的加载。
  • SpringBoot通过扩展URLClassLoader–LauncherURLClassLoader,实现了jar in jar中class文件的加载。
  • WarLauncher通过加载WEB-INF/classes目录及WEB-INF/lib和WEB-INF/lib-provided目录下的jar文件,实现了war文件的直接启动及web容器中的启动。

通过spring-boot-plugin 生成了MANIFEST.MF , main-class 指定运行java -jar的主程序把依赖的jar文件 打包在fat jar.

到此这篇关于Spring Boot如何通过java -jar启动的文章就介绍到这了,更多相关SpringBoot java -jar启动内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring Boot如何通过java -jar启动

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何通过java -jar启动Spring Boot

这篇文章给大家介绍如何通过java -jar启动Spring Boot,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。新建工程 打包 启动新创建一个Spring Boot的工程其中打包的配置为
2023-06-15

java -jar命令及SpringBoot通过java -jav启动项目的过程

本篇文章将为大家讲述关于SpringBoot项目工程完成后,是如何通过java-jar命令来启动的,以及介绍java-jar命令的详细内容,对SpringBootjava -jav启动过程感兴趣的朋友跟随小编一起看看吧
2023-05-20

通过Docker启动Solace并在Spring Boot通过JMS整合Solace的操作方法

本文将介绍如何在Spring中使用,虽然代码使用的是SpringBoot,但并没有使用相关starter,跟Spring的整合一样,可通用,JMS是通过的消息处理框架,可以深入学习一下,不同的MQ在JMS的整合上都是类似的,感兴趣的朋友跟随小编一起看看吧
2023-01-18

通过Docker安装启动DB2在Spring Boot整合DB2的方法

DB2是IBM的一款优秀的关系型数据库,这篇文章主要介绍了通过Docker启动DB2,并在Spring Boot整合DB2,需要的朋友可以参考下
2023-01-18

通过Docker安装启动DB2在Spring Boot整合DB2的方法

目录1 简介2 docker安装DB2Spring Boot整合DB2代码PS:Docker中安装DB2详细教程1 简介DB2是IBM的一款优秀的关系型数据库,简单学习一下。2 Docker安装DB2为了快速启动,直接使用Docker
2023-01-18

jar 命令启动java 指定配置文件路径 jar如何启动

可以使用以下命令来启动一个包含配置文件的jar包: java -jar yourJarFile.jar --spring.config.location=file:/path/to/your/config/file/application.
jar 命令启动java 指定配置文件路径 jar如何启动
2023-12-23

启动Spring Boot 项目失败如何解决

启动Spring Boot 项目失败如何解决?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Spring Boot 项目是不是经常失败,显示一大堆的错误信息,如端口重复绑定时
2023-06-06

如何在Spring Boot中内嵌Tomcat并启动

本篇文章给大家分享的是有关如何在Spring Boot中内嵌Tomcat并启动,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。createEmbeddedServletConta
2023-05-31

如何解决通过spring-boot-maven-pluginpackage失败问题

这篇文章主要介绍了如何解决通过spring-boot-maven-pluginpackage失败问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-15

java启动如何设置JAR包内存大小

这篇文章主要介绍了java启动如何设置JAR包内存大小问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-14

通过ssh工具启动VNC,Windows系统如何通过ssh工具启动VNC

IIS7服务器管理工具能够作为VNC的客户端,进行VNC的命令操作,可在客户端,下载,安装VNC软件!同时,它也可以作为FTP的客户端,进行FTP的命令操作!它能够批量连接Windows和Linux系统下的服务器和VPS,并能够实时监测他们
2023-06-04

Spring Boot如何通过Actuator显示git和build的信息

这篇文章主要介绍了Spring Boot通过Actuator显示git和build的信息,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-01-13

利用spring boot如何快速启动一个web项目详解

前言基于我们创建好的lion项目,使用spring boot,我们就可以通过很少的一些配置,便可以启动这个项目。下面话不多说了,来一起看看详细的介绍吧。方法如下:1 引入Spring boot,我们打开lion父模块的pom文件,继承 sp
2023-05-30

Spring Boot 2.6.x整合Swagger启动失败报错如何解决

这篇文章主要介绍了Spring Boot 2.6.x整合Swagger启动失败报错如何解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring Boot 2.6.x整合Swagger启动失败报错如何解决文
2023-06-29

如何使用spring boot整合kafka和延迟启动消费者

这篇文章给大家分享的是有关如何使用spring boot整合kafka和延迟启动消费者的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。spring boot 整合kafka,延迟启动消费者spring boot整合
2023-06-20

使用Shell脚本如何启动/停止Java的jar程序

本文介绍如何使用Shell脚本来开启和停止jar程序的后台运行,以及如何实现out大文件的切分。另外,补充一些后台运行的小知识。 启动脚本:start_upload.sh#!/bin/sh nohup java -jar UploadSch
2022-06-04

通过启动文件夹无法添加启动项如何快速添加Win8.1启动项

以前我们用Win7或编程客栈XP时,要添加启动项可以很方便地通过开始菜单程序中的启动文件夹来添加,但在Win8http://www.cppcns.com中这种方法就不行了。不过也不要灰心,Win8一样可以自己添加开机启动项php。现在系统之
2023-06-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录