字节二面:为什么SpringBoot的 jar 可以直接运行?我说因为内嵌了Tomcat容器,他让我出门左转。。

这篇具有很好参考价值的文章主要介绍了字节二面:为什么SpringBoot的 jar 可以直接运行?我说因为内嵌了Tomcat容器,他让我出门左转。。。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在传统的Java应用程序开发和部署场景中,开发者往往需要经历一系列复杂的步骤才能将应用成功部署到生产环境。例如,对于基于Servlet规范的Java Web应用,开发完成后通常会被打包成WAR格式,然后部署到像Apache Tomcat、Jetty这样的Web容器中。这一过程中,不仅要管理应用本身的编译产物,还需要处理各种第三方依赖库的版本和加载顺序,同时在服务器端进行相应的配置以确保应用正常运行。

随着Spring Boot产生,它以其开箱即用、约定优于配置的理念彻底改变了Java应用的开发体验。其中一个标志性特征便是Spring Boot应用可以被打包成一个可直接运行的jar文件,无需外部容器的支持。

当提及“Spring Boot的jar可以直接运行”,我们不禁好奇:这背后究竟是怎样的机制让一个简单的命令行操作就能启动一个完整的Web服务或任何类型的Java应用呢?本文将深入剖析Spring Boot的打包过程和运行原理,揭示其jar包是如何巧妙地集成了依赖、嵌入了Web容器、实现了自动配置等功能,从而使得开发人员能够迅速地将应用部署到任何支持Java的环境中。

SpringBoot JAR包基础概念

Fat JAR(也称作Uber JAR,也被戏称为胖Jar)是一种特殊的Java归档(JAR)文件,它将应用程序所需的全部依赖库与应用程序自身的类文件合并到了同一个JAR文件中。在Spring Boot上下文中,Fat JAR被用于构建一种完全自包含且可独立运行的应用程序包。这样的jar文件不仅仅包含项目的主代码,还包括了所有必要的第三方库、资源文件等一切运行时所需要的组件。

Fat JAR的核心特点是“自包含”,意味着只需分发这一个文件即可部署应用,无需再额外处理众多的依赖库。这种形式极大地方便了应用的快速部署与迁移,尤其适合于云端部署或者无网络环境下的安装。

而对于普通jar包来说,它通常仅包含一个模块或应用程序的一部分,主要用来封装和组织Java类及相关资源。在Java生态系统中,一个普通的jar包可能仅是一个库,或者一组相关功能的集合,但它不会包含其他依赖的jar包,因此在运行时需要与之相关的其他库一起存在于类路径中。

相比之下,Fat JAR则解决了依赖管理的问题,通过将所有的依赖都纳入其中,避免了由于类路径设置不正确导致的“缺失类”或“找不到类”的问题。在Spring Boot项目中,通过Maven或Gradle插件可以轻易地构建出这样的Fat JAR,使得最终生成的jar文件成为一个真正的“一站式”解决方案,只需使用java -jar命令就可以启动整个应用程序,无需预先配置复杂的类路径环境。

Spring Boot应用打包机制

Spring Boot应用打包机制充分利用了Maven或Gradle构建工具的强大功能,旨在简化传统Java应用的构建与部署流程。其核心在于创建一个可执行的Fat JAR,使得开发者能够轻松地将整个Spring Boot应用及其依赖项打包成单个文件,从而实现一键启动和便捷部署。

我们以Maven打包为例:

对于使用Maven构建的Spring Boot应用,spring-boot-maven-plugin是关键插件,负责处理Fat JAR的构建。在pom.xml文件中,通常会看到如下配置:

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<version>${spring-boot.version}</version>
			<configuration>
				<!-- 可选配置项,如mainClass属性指定入口类 -->
				<mainClass>${start-class}</mainClass>
			</configuration>
			<executions>
				<execution>
					<goals>
						<goal>repackage</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

通过mvn package命令,Maven首先会按照标准流程构建项目,随后spring-boot-maven-plugin会执行repackage目标,该目标会重新包装已生成的标准JAR文件,将其转换为包含所有依赖项和适当的启动器信息的Fat JAR。这样生成的JAR可以直接通过java -jar命令启动。

Spring Boot应用打包机制均确保了生成的包不仅包含了项目本身的类,还包含了运行时所必需的所有依赖库,以及一些特定的元数据(如MANIFEST.MF中的启动类信息)。这一特性大大简化了部署过程,并有助于提升应用的可移植性和维护性。Fat jar中的内容:

  • META-INF/: 包含MANIFEST.MF文件和其他元数据信息,其中Main-Class属性指向Spring Boot的启动类加载器。
  • BOOT-INF/classes/: 存放项目自身的类文件和资源文件。
  • BOOT-INF/lib/: 放置所有依赖的jar包,包括Spring Boot starter依赖以及其他第三方库。(如果项目中有静态资源文件,也会在BOOT-INF下有对应的static、templates等目录)

Spring Boot启动器与Loader机制

Spring Boot应用的jar包可以直接运行主要依赖于它的启动器以及Loader机制,而对于Loader机制主要利用MANIFEST.MF文件以及其内部类加载逻辑。

MANIFEST.MF文件是什么?

MANIFEST.MFJAR文件内的一个标准元数据文件,它包含了关于JAR包的基本信息和运行指令。在Spring Boot应用的jar包中,MANIFEST.MF尤为重要,因为它设置了Main-Class属性,指示了用于启动整个应用程序的类,这个类通常是org.springframework.boot.loader.JarLauncher或其他由Spring Boot提供的启动器类。

Main-Class属性指向的JarLauncher类是Spring Boot自定义的类加载器体系的一部分。JarLauncher继承自org.springframework.boot.loader.Launcher,专门用于启动以Fat JAR形式发布的Spring Boot应用。JarLauncher负责创建一个类加载器LaunchedURLClassLoader

当通过java -jar命令执行Spring Boot jar包时,JVM会依据MANIFEST.MF中的Main-Class启动指定的启动器。

Spring Boot的启动器类加载器LaunchedURLClassLoader首先会读取MANIFEST.MF中的附加属性,如Start-Class(标识应用的实际主类)和Spring-Boot-Lib(指向内部依赖库的位置)。

启动类加载器工作流程如下:

  1. 当启动器类加载器启动时,它会根据MANIFEST.MF中的信息来组织类路径,保证所有内部的依赖库都能正确地被加载。

  2. 加载器会区分出 BOOT-INF/classes中的应用程序类和 BOOT-INF/lib 下的依赖库,分别处理并加入到类加载器的搜索路径中。

  3. 加载器加载并执行实际的Start-Class,即应用的主类,触发Spring Boot框架的初始化和应用的启动流程。比如示例中的应用主类:com.springboot.base.SpringBootBaseApplication

Spring Boot的启动器和加载器机制有效地实现了对自包含jar包的管理和执行,我们无需关心复杂的类路径配置和依赖加载,只需通过一个简单的命令即可启动一个完整、独立运行的应用程序。

内嵌Web容器

Spring Boot的一大特色就是能够无缝整合并内嵌多种轻量级Web容器,比如:Apache TomcatJettyUndertow以及Reactor Netty(对于响应式编程模型)。内嵌Web容器的引入极大地简化了Web应用的部署流程,我们不再需要在本地或服务器上独立安装和配置Web服务器(比如以前还要在本地安装tomcat)。

当Spring Boot应用引入了spring-boot-starter-web依赖时,默认情况下会自动配置并启动一个内嵌的Web容器。在Spring Boot启动的过程中,内嵌容器作为应用的一部分被初始化并绑定到特定端口上,以便对外提供HTTP服务。

Spring Boot内嵌web容器的优点在于简化部署,通过将Web容器内置于应用中,只需分发单一的JAR文件,就能在干净的环境中运行应用,避免了与现有Web服务器版本冲突或配置不当等问题;同时加快了启动速度,尤其在开发和测试阶段,实现近乎即时的热重启;提高了应用的稳定性,因为开发环境和生产环境使用相同的Web容器,降低了因环境差异导致的问题;此外,虽然容器是内嵌的,但仍然可以进行全面的配置调整,如端口、连接数、SSL设置等,以满足不同场景的需求。通过内嵌Web容器,Spring Boot真正实现了“开箱即用”的理念。

自动配置与类路径扫描

Spring Boot的核心特性之一就是其强大的自动配置能力,它允许应用在几乎零配置的情况下快速启动并运行。

当应用启动时,Spring Boot会读取resource/META-INF/spring.factories文件,该文件列出了所有可用的自动配置类。当它检测到应用环境中对应的自动配置类就会生效,通过@Configuration注解的类创建并注册Bean到Spring容器中,从而实现Bean的自动装配。

这里说明下,在springboot3.x以后,就不在从resource/META-INF/spring.factories读取自动配置类了,而是从org.springframework.boot.autoconfigure.AutoConfiguration.imports中读取,这一点请参考文章:华为二面:SpringBoot如何自定义_Starter_?

并且Spring Boot还采用条件注解(如@ConditionalOnClass@ConditionalOnMissingBean等)来智能判断何时应用特定的配置。这些注解可以根据类路径中是否存在特定类、系统属性或环境变量的值等因素,决定是否应该激活某个自动配置类。这意味着只有当满足特定条件时,相应的Bean才会被创建和注入。

而对于应用主类则是用@SpringBootApplication注解标识。@SpringBootApplication是一个复合注解,包含了@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个注解的功能。其中

  • @SpringBootConfiguration是一个Spring配置类,可以替代@Configuration注解,声明当前类是Spring配置类,里面包含了一系列@Bean方法或@ConfigurationProperties等配置。
  • @EnableAutoConfiguration启用自动配置特性,告诉Spring Boot根据应用类路径中的依赖来自动配置Bean。Spring Boot会根据类路径扫描的结果,智能地决定哪些自动配置类应当生效。
  • @ComponentScan会自动扫描和管理Spring组件,包括@Service、@Repository、@Controller和@Component等注解标注的类。通过该注解,Spring Boot能自动发现和管理应用中的各个组件,并将其注册为Spring容器中的Bean。

通过上述机制,Spring Boot能够智能识别项目依赖、自动配置Bean,并结合类路径扫描确保所有相关的组件和服务都被正确地初始化和管理,我们就可以专注于业务逻辑的开发,而不必过多考虑基础设施层面的配置问题。

总结

Spring Boot 应用程序被打包成的jar包之所以可以直接通过 java -jar 命令运行,是因为Spring Boot在构建过程中做了一些特殊的设计和配置。具体原因:

  1. Fat/Uber JAR: Spring Boot使用maven插件spring-boot-maven-plugin(或Gradle对应的插件)将项目及其所有依赖项打包成一个单一的、自包含的jar文件,通常称为“Fat JAR”或“Uber JAR”。这意味着不仅包含了自己的类文件,还包含了运行应用所需的所有第三方库。

  2. Manifest.MF: 在打包过程中,此插件会修改MANIFEST.MF文件,这是jar包中的一个元数据文件。在MANIFEST.MF中,特别指定了Main-Class属性,该属性指向Spring Boot的一个内置的启动类(如org.springframework.boot.loader.JarLauncher),这个启动器类知道如何正确启动Spring Boot应用程序。

  3. 嵌入式Servlet容器:Spring Boot默认集成了诸如Tomcat、Jetty或Undertow等嵌入式Web容器,使得无需外部服务器环境也能运行Web应用。

  4. 启动器类加载器:当通过java -jar运行Spring Boot应用时,JVM会根据MANIFEST.MF中的Main-Class找到并运行指定的启动器类。这个启动器类加载器能够解压并加载内部的依赖库,并定位到实际的应用主类(在spring-boot-starter-parent@SpringBootApplication注解标记的类),进而执行其main方法。

  5. 类路径扫描和自动配置:Spring Boot应用通过特定的类路径扫描机制和自动配置功能,能够在启动时识别出应用所依赖的服务和组件,并自动配置它们,大大简化了传统Java应用的配置和部署过程。

Spring Boot通过精心设计的打包流程和启动器类,使得生成的jar包可以直接作为一个独立的应用程序运行,极大地简化了部署和运维复杂度。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等文章来源地址https://www.toymoban.com/news/detail-844136.html

到了这里,关于字节二面:为什么SpringBoot的 jar 可以直接运行?我说因为内嵌了Tomcat容器,他让我出门左转。。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 字节一面:post 为什么会发送两次请求?被问懵了…

    字节一面:post 为什么会发送两次请求?被问懵了…

    前言 最近博主在 字节面试 中遇到这样一个面试题,这个问题也是前端面试的高频问题,因为在前端开发的日常开发中我们总是会与post请求打交道,一个小小的post请求也是牵扯到很多知识点的,博主在这给大家细细道来。 同源策略 在浏览器中,内容是很开放的,任何资源都

    2024年02月08日
    浏览(7)
  • 神经网络为什么可以学习

    神经网络为什么可以学习

    本资料转载于B站up主:大模型成长之路,仅用于学习和讨论,如有侵权请联系 动画解析神经网络为什么可以学习_哔哩哔哩_bilibilis 1.1 也可以是一层,也可以是多层  2.1 每两个神经元之间有两个参数,我们称之为权重 3.1 4.1 5.1 6.1 7.1 8.1 9.1 10.1 11.1 12.1 13.1 14.1 15.1 16.1  17.1 18.1

    2024年02月12日
    浏览(10)
  • WebSocket 是什么原理?为什么可以实现持久连接?

    WebSocket 是什么原理?为什么可以实现持久连接?

    WebSocket 是一种用于实现持久连接的通信协议,它的原理和工作方式相对复杂,但我们可以尝试以尽可能简单和清晰的方式来解释它。 WebSocket 的原理 在理解 WebSocket 的工作原理之前,我们首先要了解 HTTP 协议的短连接性质。在传统的 HTTP 通信中,客户端发送一个请求到服务器

    2024年02月05日
    浏览(11)
  • 为什么华为、阿里、字节跳动、微软等都走上了云原生和数字化之路?

    为什么华为、阿里、字节跳动、微软等都走上了云原生和数字化之路?

    亲爱的开发者朋友们好哇, 前几天我发了篇文章,请各位朋友帮忙给最新一期的《新程序员》选封面,并且和大家说内容已经全部完成,即将出版和大家正式见面。今天,它来啦!《新程序员003:云原生和全面数字化实践》正式开启预售,现在下单,在元旦后将正式开放电子

    2024年02月05日
    浏览(14)
  • 为什么单片机可以直接烧录程序的原因是什么?

    为什么单片机可以直接烧录程序的原因是什么?

    单片机(Microcontroller)可以直接烧录程序的原因主要有以下几点: 集成性:单片机是一种高度集成的芯片,内部包含了处理器核心(CPU)、存储器(如闪存、EEPROM、RAM等)、输入/输出接口(如GPIO、UART、SPI、I2C等)以及时钟电路等功能模块。这种高度集成的设计使得单片机能

    2024年02月16日
    浏览(45)
  • 20 信任链:为什么可以相信一个 HTTPS 网站?

    20 信任链:为什么可以相信一个 HTTPS 网站?

    现实的生活当中,如果想证明一份合同没有被修改过,人们会在合同上盖一个齐缝章,并附上自己的签名。签名和盖章其实是一个含义,目的是证明自己签署过某份协议,而且一经签署,协议就不能再变更。 如果想阻止一份合同被修改,最容易想到的方式是加密 。合同一旦

    2024年02月21日
    浏览(12)
  • 路由器劫持是什么意思为什么要劫持路由器有哪些方法可以防范

    继“棱镜门”事件之后,网络安全也随之被各大媒体关注,近段时间有不少媒体报道,全球拥有大量的路由器遭入侵、路由器被劫持等等。另外在如今越来越多的无线网络环境中,蹭网也是常常被人们提及,那么路由器劫持是什么意思?怎么看路由器是否被劫持?针对这两个

    2024年02月07日
    浏览(17)
  • 手机wifi可以连接路由器但是上不了网,为什么?

    手机wifi可以连接路由器但是上不了网,为什么?

    本文转载自:路由器知识库:www.luyouqiset.cn 详细介绍无线路由器设置后却上不了网的解决办法。但我们拿到路由器后一般都是按照说明书,一步一步登陆后台去设置无线路由器。结果辛辛苦苦设置完后,发现手机,笔记本电脑都够连接到tplink路由器的网络但上不了网。气愤到先

    2024年02月11日
    浏览(12)
  • MySQL可重复读隔离级别下,乐观锁为什么可以生效?

    今天蹦出一个很蠢的问题,来记录一下 MySQL在可重复读事务隔离级别下,通过版本号实现的乐观锁可以生效吗? 举个例子:开启了两个事务一和二,在事务一中对某条数据进行了修改,版本号发生变化。但是在事务二中,查询这条记录的版本号,并没有发生变化,怎么确定这

    2024年02月07日
    浏览(10)
  • 为什么网络可以ping通,还是不能ssh到目标主机?

      做运维工作,我们都是通过远程的方式去连接一台服务器或者虚拟机,很多初次做运维的朋友可能经常会遇到这样一个问题:我ping目标服务器的IP是通的,但是我通过ssh却不能连上主机。今天,就来浅谈一下,为什么你ping网络是通的,但是却不能连接到远程主机,当你弄

    2024年02月09日
    浏览(17)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包