当前位置: 首页 > news >正文

【七】SpringBoot为什么可以打成 jar包启动

SpringBoot为什么可以打成 jar包启动

简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一下了,不至于下次想不来了又去翻资料。

一、SpringBoot生成的jar包是什么

        Spring Boot的可执行jar包又称作“fat jar”,那什么是fat jar呢?在java中,将应用程序及其依赖jar一起打包到一个独立的jar中,就叫fat jar,它也叫uberJar。springboot的打包方式就是这样,将应用程序代码打包到BOOT-INF.classes,将依赖包打包到BOOT-INF.lib目录,这里我们以xxl-job-admin-2.4.0-SNAPSHOT.jar为例来做说明,我们使用反编译工具jd将jar打开,目录如下:

各目录存放内容如下:

BOOT-INF/classes:目录存放应用编译后的class文件。
BOOT-INF/lib:目录存放应用依赖的第三方JAR包文件。
META-INF:目录存放应用打包信息(Maven坐标、pom文件)和MANIFEST.MF文件。
org:目录存放SpringBoot相关class文件。

这里我们首先关注一下配置文件:MANIFEST.MF,内容如下:

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Archiver-Version: Plexus Archiver
Built-By: user
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.xxl.job.admin.XxlJobAdminApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.6.7
Created-By: Apache Maven 3.6.1
Build-Jdk: 1.8.0_211
Main-Class: org.springframework.boot.loader.JarLauncher

参考 Oracle 官方对该的说明:

Main-Class:Java 规定的 jar 包的启动类,这里设置为 spring-boot-loader 项目的 JarLauncher 类,进行 Spring Boot 应用的启动

Start-Class:Spring Boot 规定的主启动类,这里通过 Spring Boot Maven Plugin 插件打包时,会设置为我们定义的 Application 启动类

为什么不直接将我们的 Application 启动类设置为 Main-Class 启动呢?

        因为通过 Spring Boot Maven Plugin 插件打包后的 jar 包,我们的 .class 文件在 BOOT-INF/classes/ 目录下,在 Java 默认的 jar 包加载规则下找不到我们的 Application 启动类,也就需要通过 JarLauncher 启动加载。当然,还有一个原因,Java 规定可执行器的 jar 包禁止嵌套其它 jar 包,在 BOOT-INF/lib 目录下有我们 Spring Boot 应用依赖的所有第三方 jar 包,因此spring-boot-loader 项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader,支持加载 BOOT-INF/classes 目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。

二、JarLauncher启动器实现原理

上文描述了Application  的Main-Clas启动类是JarLauncher 类,那么接下来我们一起来看看 Spring Boot 的 JarLauncher 这个类

JarLauncher的继承关系如下:

JarLauncher全路径是org.springframework.boot.loader.JarLauncher

public class JarLauncher extends ExecutableArchiveLauncher {
 
    private static final String DEFAULT_CLASSPATH_INDEX_LOCATION = "BOOT-INF/classpath.idx";
 
    static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
        if (entry.isDirectory()) {
            return entry.getName().equals("BOOT-INF/classes/");
        }
        return entry.getName().startsWith("BOOT-INF/lib/");
    };
 
    public JarLauncher() {
    }
 
    protected JarLauncher(Archive archive) {
        super(archive);
    }
 
    @Override
    protected ClassPathIndexFile getClassPathIndex(Archive archive) throws IOException {
        // Only needed for exploded archives, regular ones already have a defined order
        if (archive instanceof ExplodedArchive) {
            String location = getClassPathIndexFileLocation(archive);
            return ClassPathIndexFile.loadIfPossible(archive.getUrl(), location);
        }
        return super.getClassPathIndex(archive);
    }
 
    private String getClassPathIndexFileLocation(Archive archive) throws IOException {
        Manifest manifest = archive.getManifest();
        Attributes attributes = (manifest != null) ? manifest.getMainAttributes() : null;
        String location = (attributes != null) ? attributes.getValue(BOOT_CLASSPATH_INDEX_ATTRIBUTE) : null;
        return (location != null) ? location : DEFAULT_CLASSPATH_INDEX_LOCATION;
    }
 
    @Override
    protected boolean isPostProcessingClassPathArchives() {
        return false;
    }
 
    @Override
    protected boolean isSearchCandidate(Archive.Entry entry) {
        return entry.getName().startsWith("BOOT-INF/");
    }
 
    @Override
    protected boolean isNestedArchive(Archive.Entry entry) {
        return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
    }
 
    public static void main(String[] args) throws Exception {
        new JarLauncher().launch(args);
    }
 
}

通过new一个JarLauncher().launch(args)方式进行启动。

public class Jarlauncher{
    ...
    private static final String JAR_MODE_LAUNCHER = "org.springframework.boot.loader.jarmode.JarModeLauncher";
 
    ...
    protected void launch(String[] args) throws Exception {
        //判断是否以一个分解模式的方式运行,如果是则运行,否则只支持规范的jar文件从而选择跳过
        if (!isExploded()) {
            JarFile.registerUrlProtocolHandler();
        }
        //获取类加载器
        ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
        //获取系统中的jar模型
        String jarMode = System.getProperty("jarmode");
        //加载启动引导类
        String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER :             
        getMainClass();
        //启动应用
        launch(args, launchClass, classLoader);
    }
}

相关文章:

【七】SpringBoot为什么可以打成 jar包启动

SpringBoot为什么可以打成 jar包启动 简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一…...

031-第三代软件开发-屏幕保护

第三代软件开发-屏幕保护 文章目录 第三代软件开发-屏幕保护项目介绍屏幕保护 关键字: Qt、 Qml、 MediaPlayer、 VideoOutput、 function 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Object Language&#…...

Ubuntu 22.04 更新完内核重启卡在 grub 命令行解决办法

倒霉伊始 升级内核过程中出现如下警告,然后重启引导失败: Warning: os-prober will not be executed to detect other bootable partitions 屏幕内容如下: GNU GRUB version 2.06Minimal BASH-like line editing is supported. For the fir…...

Stream流式处理

Stream流式处理: 建立在Lambda表达式基础上的多数据处理技术。 可以对集合进行迭代、去重、筛选、排序、聚合等处理,极大的简化了代码量。 Stream常用方法 Stream流对象的五种创建方式 //基于数组 String[] arr {"a","b","c…...

ROG STRIX GS-AX5400 使用笔记

1. 技术支持 咨询京东&#xff08;因为是在京东购买的&#xff09; 2. 增强信号设置 Note&#xff1a;关于设置的具体步骤&#xff0c;请参考教程《华硕路由器地区如何改成澳大利亚》。 操作路径&#xff1a;打开主页<192.168.50.1> ⇒ 输入用户名和密码后选择登录 ⇒ …...

【刷题-PTA】堆栈模拟队列(代码+动态图解)

【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2&#xff0c;请用这两个堆栈模拟出一个队列Q。 …...

FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法

正常代码&#xff0c;部署到server 2012时&#xff0c;在上传音频mp3文件时&#xff0c;显示错误“不支持给定路径的格式”&#xff0c;上传控件使用FileUpload控件&#xff1a; 因为程序之前是正常的&#xff0c;因此应该不是程序的问题。 上传时&#xff0c;发现在选择文件时…...

这两天的一些碎碎念

一直以来我都不算是一个非常热爱运维岗位的一个人&#xff0c;其实本行工作对于我来说只是一个工作。运维的广度很大&#xff0c;说什么工作了7年了&#xff0c;可最终总感觉还曾是一窍不通。 什么shell啊&#xff0c;什么python啊&#xff0c;什么大数据啊&#xff0c;7年里&a…...

Unity 最新DOTS系列之《Baking与Baker的详解》

Unity DOTS Baking与Baker详解 最近DOTS终于发布了正式的版本, 我们来分享一下DOTS里面Baking 与Baker的关键概念&#xff0c;方便大家上手学习掌握Unity DOTS开发。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀&…...

【Tensorflow 2.12 简单智能商城商品推荐系统搭建】

Tensorflow 2.12 简单智能商城商品推荐系统搭建 前言架构数据召回排序部署调用结尾 前言 基于 Tensorflow 2.12 搭建一个简单的智能商城商品推荐系统demo~ 主要包含6个部分&#xff0c;首先是简单介绍系统架构&#xff0c;接着是训练数据收集、处理&#xff0c;然后是召回模型、…...

Unity 单例-接口模式

单例-接口模式 使用接口方式实现的单例可以继承其它类&#xff0c;更加方便 using System.Collections; using System.Collections.Generic; using UniRx; using UniRx.Triggers; using UnityEngine; namespace ZYF {public interface ISingleton<TMono> where TMono : M…...

【Java 进阶篇】Java XML解析:从入门到精通

XML&#xff08;可扩展标记语言&#xff09;是一种常用的数据格式&#xff0c;用于存储和交换数据。在Java中&#xff0c;XML解析是一项重要的任务&#xff0c;它允许您从XML文档中提取和操作数据。本篇博客将从基础开始&#xff0c;详细介绍如何在Java中解析XML文档&#xff0…...

【图像配准】Canny边缘检测+模板配准红外可见光双路数据

研究目的 最近在做无人机遥感红外和可见光双路数据配准&#xff0c;由于红外相机视野范围较小&#xff0c;因此配准的目的主要是在可见光的视野范围内&#xff0c;裁剪出红外图像对应的部分&#xff0c;同时&#xff0c;保持可见光的高分辨率不变。 本文思路 本文尝试使用Ca…...

关于单机流程编排技术——docker compose安装使用的问题

最近在学习docker相关的东西&#xff0c;当我在docker上部署了一个nest应用&#xff0c;其中该应用中依赖了一个基于mysql镜像的容器&#xff0c;一个基于redis镜像的容器。那我&#xff0c;当我进行部署上线时&#xff0c;在启动nest容器时&#xff0c;必须保证redis容器和mys…...

Google Chrome的新“IP保护”功能将隐藏用户的IP地址

导语&#xff1a;在保护用户隐私方面&#xff0c;Google Chrome正在测试一项名为“IP保护”的新功能。通过使用代理服务器掩盖用户的IP地址&#xff0c;这项功能能够增强用户的隐私保护。在意识到IP地址可能被用于秘密追踪后&#xff0c;Google希望在确保用户隐私的同时&#x…...

做机器视觉工程师,苏州德创能不能去工作?

每一家公司都有自身特点&#xff0c;同时也每一家都有自身的bug。 苏州德创作为美国康耐视Cognex产品在华东最大的代理商&#xff0c;也是康耐视外包团队。那么苏州德创有哪些业务构成&#xff0c;业务的构成也是其招聘的主要人员的方向。 设备视觉供应商&#xff0c;如卓越&…...

交换机基础(二):VLAN 基础知识

一、VLAN 基础知识 虚拟局域网 (Virtual Local Area Network,VLAN) 是一种将局域网设 备从逻辑上划分成一个个网段&#xff0c;从而实现虚拟工作组的数据交换技术。 这一技术主要应用于3层交换机和路由器中&#xff0c;但主流应用还是在3层交换机中。 VLAN 是基于物理网络上构建…...

一个基于Vue3搭建的低代码数据可视化开发平台

JNPF是一个Vue3搭建的低代码数据可视化开发平台&#xff0c;将图表或页面元素封装为基础组件&#xff0c;无需编写代码即可完成业务需求。 在JNPF中&#xff0c;至少包含表单建模、流程设计、报表可视化、代码生成器、系统管理、前端UI等组件&#xff0c;这种情况下我们避免了重…...

经验风险最小化与结构风险最小化:优化机器学习模型的两种方法

随着大数据时代的到来&#xff0c;机器学习在各个领域中的应用越来越广泛。然而&#xff0c;在构建机器学习模型时&#xff0c;我们面临着两个主要的挑战&#xff1a;经验风险最小化和结构风险最小化。本文将深入探讨这两种方法&#xff0c;并分析它们在优化机器学习模型中的作…...

Java泛型中的问号是什么意思

通配符概念 因为 List 是泛型类&#xff0c;为了 表示各种泛型 List 的父类&#xff0c;可以使用类型通配符&#xff0c;类型通配符使用问号(?)表示&#xff0c;将一个问号当做类型元素传递个 List&#xff0c;可以表示为 List<?>,意思是 元素类型未知的 List&#xf…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...