1-SpringBoot工程打包后为何执行Java -Jar就能启动?

news2024/12/28 2:48:00

本文主要分享SpringBoot工程项目如何打包成一个可直接通过java -jar执行的jar,并且简单分析其启动步骤原理。

文章目录

    • 1.SpringBoot如何打包成一个可执行jar?
    • 2.SpringBoot打包成的jar为何可以直接Java -jar执行?
    • 3.一窥SpringBoot初启动

1.SpringBoot如何打包成一个可执行jar?

SpringBoot打包成成一个可执行jar需要依赖一个maven打包插件spring-boot-maven-plugin,如下所示在pom文件结尾的build节点添加依赖,同时将src/main/javasrc/main/resources打入jar里面。

 	<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

此时再执行maven package打包成jar(最后打包完成在target目录下):
在这里插入图片描述
此时我们直接通过cmd控制台执行命令:java -jar boot-first-1.0.0-SNAPSHOT.jar 可以看到启动成功。
在这里插入图片描述
相关日志已经打印出来了,此时我们再通过postman验证一下编码中的接口是否生效:

在这里插入图片描述
程序中的接口代码如下:
在这里插入图片描述

2.SpringBoot打包成的jar为何可以直接Java -jar执行?

java程序的入口是main方法 ,如果一个jar能够通过java -jar执行起来,肯定离不开main方法。那springboot通过spring-boot-maven-plugin插件打包成的jar通常称为fat jar,里面不仅仅包含了程序代码还包括其他引用的依赖的jar。那这个fat jar的main方法入口在哪儿呢?我们通过解压该jar可以看到,在META-INF下的有一个MANIFEST.MF文件:
在这里插入图片描述
其中MANIFEST.MF中内容如下,其中比较重要的几行信息:

Start-Class: com.xren.bootfirst.BootFirstApplication ##程序开始类
Spring-Boot-Classes: BOOT-INF/classes/		##加载的class
Spring-Boot-Lib: BOOT-INF/lib/   ##加载的内部jar位置
Main-Class: org.springframework.boot.loader.JarLauncher  ## boot启动类

在这里插入图片描述

综上可知,SpringBoot项目通过引入打包插件spring-boot-maven-plugin生成特定的主清单文件MANIFEST.MF,其中包含了程序的入口类和相关启动依赖。

3.一窥SpringBoot初启动

通用的jar包如果能被java -jar执行,只需要其MANIFEST.MF文件中有 Main-Class配置项即可。而此处SpringBoot的jar中的MANIFEST.MF文件里面不仅仅包含Main-Class 还有Start-Class、Spring-Boot-Classes、Spring-Boot-Lib等相关信息,那他们是如何运行的呢?我们还是要回到该jar主类org.springframework.boot.loader.JarLauncher来一窥究竟。

查看org.springframework.boot.loader.JarLauncher类需要pom引入spring-boot-loader,如下:

	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
            <version>2.2.7.RELEASE</version>
	</dependency>

引入后其中JarLaunche类代码如下,它包含一个main方法,并且继承一个父类ExecutableArchiveLauncher。

public class JarLauncher extends ExecutableArchiveLauncher {
    static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
    static final String BOOT_INF_LIB = "BOOT-INF/lib/";
    public JarLauncher() {
    }
    protected JarLauncher(Archive archive) {
        super(archive);
    }
    protected boolean isNestedArchive(Entry entry) {
        return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
    }
    public static void main(String[] args) throws Exception {
        (new JarLauncher()).launch(args);
    }
}

从main方法进入我们需要关注一个launch方法,其中的三行

  1. 注册一个jar处理类
  2. 获取一个类加载器(通过路径加载lib和classs)
  3. 通过获取主启动类和类加载器运行启动。
	 protected void launch(String[] args) throws Exception {
        JarFile.registerUrlProtocolHandler();
        ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
        this.launch(args, this.getMainClass(), classLoader);
    }

追寻this.getMainClass() 方法,其会指向上述的父类ExecutableArchiveLauncher中,此处就回到了我们开始说的Start-Class配置项。

	protected String getMainClass() throws Exception {
        Manifest manifest = this.archive.getManifest();
        String mainClass = null;
        if (manifest != null) {
            mainClass = manifest.getMainAttributes().getValue("Start-Class");
        }
        if (mainClass == null) {
            throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
        } else {
            return mainClass;
        }
    }

getMainClass()方法中的this.archive.getManifest() 就是获取jar下的META-INF/MANIFEST.MF文件。此处追寻到Archive的子类ExplodedArchive中,如下代码获取清单文件:

 	private File getManifestFile(File root) {
        File metaInf = new File(root, "META-INF");
        return new File(metaInf, "MANIFEST.MF");
    }

到此时清单文件中的Start-Class、Spring-Boot-Classes、Spring-Boot-Lib都已找到,准备启动!
回到org.springframework.boot.loader.Launcher的launch方法。

  1. 当前线程设置类加载器
  2. 创建主方法并运行(主方法即为Start-Class指向的类)
	protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
        Thread.currentThread().setContextClassLoader(classLoader);
        this.createMainMethodRunner(mainClass, args, classLoader).run();
    }

以上两行代码具体实现在org.springframework.boot.loader.MainMethodRunner类中,run方法即为最后的启动逻辑

  1. 通过当前线程类加载器载入清单文件Start-Class配置的主类。
  2. 加载后获取主类中的main方法(XXXApplication中的main方法)。
  3. 反射执行该XXXApplication中的main方法。
	public MainMethodRunner(String mainClass, String[] args) {
        this.mainClassName = mainClass;
        this.args = args != null ? (String[])args.clone() : null;
    }

    public void run() throws Exception {
        Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
        Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
        mainMethod.invoke((Object)null, this.args);
    }

至此,从执行java -jar命令,SpringBoot打包的jar启动到程序中XXXApplication中的main方法这一阶段我们就初步分析完了!

总结:SpringBoot通过引入打包插件spring-boot-maven-plugin将其相关信息写入到java-jar的META-INF/MANIFEST.MF文件中,而后通过清单文件中的主入口Main-Class配置的org.springframework.boot.loader.JarLauncher类加载相关class、lib和应用程序主类Start-Class配置的XXXApplication类,再反射获取XXXApplication类中我们业务定义的main方法启动运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业直播该如何做?硬件设备、网络环境、设备连接和观看权限等整个直播流程教程

这是一份面向直播新手的企业直播说明教程&#xff0c;字数较多&#xff0c;完整看完&#xff0c;可能会需要求10分钟&#xff0c;建议您可以【收藏】&#xff0c;如果本文章对您有帮助&#xff0c;就帮助【点个赞】吧~~~ 阿酷TONY / 2023-5-12 / 原创文章 / 长沙 / 文章…

今天的CRM系统,还仅仅是一套营销工具吗?

如今&#xff0c;数字化浪潮正席卷全球&#xff0c;数字经济已经成为全球经济未来发展的新动能和新方向。据IDC的预测显示&#xff0c;全球对数字转型的投资将以每年17.1%的复合速度增长&#xff0c;2023年这类投资预计将达到2.3万亿美元。而CRM系统&#xff08;客户关系管理系…

用友NC软件被locked1勒索病毒攻击加密的方式,服务器oracle数据库中了勒索病毒

用友NC软件是一款企业级管理软件&#xff0c;广泛应用于各行各业的集团企业中。然而&#xff0c;最近有关locked1勒索病毒对用友NC软件的攻击已经引起了广泛的关注和担忧。下面将为大家介绍一下有关locked1攻击加密用友NC软件的方式&#xff0c;并给出合理的解决方案。 首先&a…

Kafka 之生产者与消费者基础知识:基本配置、拦截器、序列化、分区器

一、配置 1. 必须要配置的参数&#xff1a; kafaf集群地址列表&#xff1a;理论上写一个节点地址&#xff0c;就相当于绑定了整个kafka集群了&#xff0c;但是建议多写几个&#xff0c;如果只写一个&#xff0c;万一宕机就麻烦了kafka消息的key和value要指定序列化方法kafka对…

2023年湖北省建设厅特种作业操作证报名条件是什么?

建筑施工特种作业人员是指在房屋建筑和市政工程施工活动中&#xff0c;从事可能对本人、他人及周围设备设施的安全造成重大危害作业的人员。建筑施工特种作业人员必须经建设主管部门考核合格&#xff0c;取得建筑施工特种作业人员操作资格证书&#xff08;以下简称“资格证书”…

零代码基础,一分钟教你快速搭建微信 ChatGPT 机器人

零代码基础,一分钟教你快速搭建微信 ChatGPT 机器人 1.注册 Railway 账号:2.部署3. 配置1)点击 Configure2)选择仓库地址3)点击安装4)配置信息如下图:5)部署:deploy6)部署完成:7)微信扫码登录8) 根据需要修改配置:最后总结:效果预览:致谢开源项目:本教程收集于…

U盘分区合并的方法有哪些?

大多数用户在硬盘的使用中会进行分区操作&#xff0c;同时为了方便整理&#xff0c;部分用户也会选择给自己的U盘分区&#xff0c;可是在后续操作中发现U盘分区没有什么用处&#xff0c;因此就想要重新将其合并&#xff0c;但要把分区重新合并没那么容易&#xff0c;那么U盘被分…

笔试练习Day02

一.选择题&#xff1a; 1.A 派生出子类 B &#xff0c; B 派生出子类 C &#xff0c;并且在 java 源代码有如下声明&#xff1a; 1. A a0new A(); 2. A a1new B(); 3. A a2new C(); 问以下哪个说法是正确的&#xff08;&#xff09; A 只有第一行能通过编译 B 第1、2行能通过编…

mysql distinct 和 group by 去重

标题mysql distinct 和 group by 去重 一、先说结论&#xff1a; MySQL中常用去重复数据的方法是使用 distinct 或者 group by group by 分组后&#xff0c;如果没有对分组后的数据进行操作&#xff0c;如使用聚合函数/分组函数&#xff1a;count、sum、avg、max 、min&…

封装Python脚本:使用pymysql+sshtunnel,支持通过SSH隧道方式链接mysql数据库

一、前言 通常为了保证数据库安全&#xff0c;不会允许直接连接数据库&#xff0c;而是需要通过SSH隧道去连接服务器背后的数据库&#xff1b;通过Navicat操作如下&#xff1a; 二、python封装脚本 # -*- coding: utf-8 -*- # Time : 2023/5/12 11:04 # Author : chen…

Activity内存泄漏时包含的view还有没有的救?

Activity泄漏会导致该Activity引用到的Bitmap、DrawingCache等无法释放&#xff0c;对内存造成大的压力&#xff0c;兜底回收是指对于已泄漏Activity&#xff0c;尝试回收其持有的资源&#xff0c;泄漏的仅仅是一个Activity空壳&#xff0c;从而降低对内存的压力。 做法也非常简…

ssh终端工具推荐-WindTerm

什么是WindTerm 官方github https://github.com/kingToolbox/WindTerm A Quicker and better SSH/Telnet/Serial/Shell/Sftp client for DevOps. 按官方说明&#xff0c;WindTerm是一个更快更好的SSH/Telnet/Serial/Shell/Sftp的DevOps工具。 WindTerm目前对商业是免费无限制…

QML APP开发套路(二):前/后端交互概述

&#xff08;1&#xff09;QML开发简介 Qt应用框架在传统UI&#xff08;QWidget窗体&#xff09;的基础上&#xff0c;提供了Qt Quick模块&#xff0c;该模块基于 QML 语言来定义UI及交互方式。区别于 QWidget 定义UI的方式&#xff0c;QML利于将UI交互与业务逻辑处理剥离成前…

什么是智慧校园?

什么是智慧校园&#xff1f; 智慧校园平台是目前教育信息化领域的热点之一。 随着数字化转型的加速&#xff0c;越来越多的学校开始寻求解决方案&#xff0c;以提高教育管理的效率和质量。 在使用智慧校园平台的过程中&#xff0c;一些痛点问题也浮现出来。为解决这些问题&a…

基于AT89C51单片机的贪吃蛇游戏设计

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87778030 源码获取 主要内容: 设计一个贪吃蛇游戏,使其具有以下游戏规则:①当没有改变方向时,贪吃蛇沿原来路径一直前进②贪吃蛇无法回头,只能异于当前方向改变行动③蛇…

网页更新提醒是什么?如何自动监控网页并自动记录或发送通知?

网页更新提醒是什么&#xff1f; 在互联网信息资源丰富&#xff0c;且更新速度快的情况下&#xff0c;如果需要监控一些网页变化&#xff0c;实现例如热点/热搜/热评监测、商品上新/价格/库存监测、作品上新/评论/点赞监测、招标/投标/拍卖/竞价监测、公告/通知/活动监测等情况…

南京大学主办 | EIScopus检索 | 2023年人工智能与统计学前沿国际会议

2023年人工智能与统计学前沿国际会议 会议简介 Brief Introduction 2023年人工智能与统计学前沿国际会议(CFAIS 2023) 会议时间&#xff1a;2023年8月18日-20日 召开地点&#xff1a;中国南京 大会官网&#xff1a;www.cfais.org 2023年人工智能与统计学国际会议(CFAIS 2023)将…

测评,补单是什么神仙利器?能提高速卖通,国际站,newegg店铺的销量

测评&#xff0c;补单相信这个词对于大部分卖家来说&#xff0c;想必都不陌生&#xff0c;因为都知道它能够快速帮助自己的产品添加评论&#xff0c;获取排名&#xff0c;打造爆款。于是许多卖家潜意识里认为“测评其实就是刷评”。 简单点来说&#xff0c;测评就是卖家通过【评…

485测试

注意如果十六进制发送数据 数据打印时 如果是%c 打印出来的数据会十进制显示 解决方案 1.改变打印方式 %x 打印 2.因为什么也不勾选 &#xff0c;默认asii显示 利用串口助手发ltz &#xff0c;打印也是ltz&#xff08;发什么就显示什么&#xff09; 如下图所示

如何使用appuploader制作描述文件​

如何使用appuploader制作描述文件​ 承接上文我们讲述了怎么制作证书&#xff0c;本文我们来看下怎么制作描述文件吧。​制作描述文件前我们首先我们来添加一个测试设备&#xff0c;后面再制作描述文件。 1.添加测试设备​ 其中添加设备一项中&#xff0c;根据提示操作添加…