SpringBoot 究竟是如何跑起来的?

电动汽车双层优化调度:架构设计与工程实践 电力系统优化调度是智能电网的核心技术,其本质是通过数学建模与算法设计实现资源的最优配置。双层优化作为一种分层决策框架,上层解决全局资源分配问题,下层处理分布式单元控制,特别适合电动汽车规模化接入场景。在工程实现层面,需要结合边缘计算设备(如NVIDIA Jetson)的实时处理能力,以及改进的通信协议(如IEEE C37.118.2)确保数据传输可靠性。实际部署中,负荷预测算法精度和实时定价机制直接影响用户体验,采用LSTM网络可将预测误差控制在8%以内。该技术已在华东示范区验证,使充电站收益提升28%,为 阅读详情

不得不说 SpringBoot 太复杂了,我本来只想研究一下 SpringBoot 最简单的 HelloWorld 程序是如何从 main 方法一步一步跑起来的,但是这却是一个相当深的坑。你可以试着沿着调用栈代码一层一层的深入进去,如果你不打断点,你根本不知道接下来程序会往哪里流动。这个不同于我研究过去的 Go 语言、Python 语言框架,它们通常都非常直接了当,设计上清晰易懂,代码写起来简单,里面的实现同样也很简单。但是 SpringBoot 不是,它的外表轻巧简单,但是它的里面就像一只巨大的怪兽,这只怪兽有千百只脚把自己缠绕在一起,把爱研究源码的读者绕的晕头转向。但是这 Java 编程的世界 SpringBoot 就是老大哥,你却不得不服。即使你的心中有千万头草泥马在奔跑,但是它就是天下第一。如果你是一个学院派的程序员,看到这种现象你会怀疑人生,你不得不接受一个规则 —— 受市场最欢迎的未必就是设计的最好的,里面夹杂着太多其它的非理性因素。

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

 

经过了一番痛苦的折磨,我还是把 SpringBoot 的运行原理摸清楚了,这里分享给大家。

Hello World

首先我们看看 SpringBoot 简单的 Hello World 代码,就两个文件 HelloControll.java 和 Application.java,运行 Application.java 就可以跑起来一个简单的 RESTFul Web 服务器了。

 

 


 
// HelloController.java package hello; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; @RestController public class HelloController { @RequestMapping("/") public String index() { return "Greetings from Spring Boot!"; } } // Application.java package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 

当我打开浏览器看到服务器正常地将输出呈现在浏览器的时候,我不禁大呼 —— SpringBoot 真他妈太简单了。

 

但是问题来了,在 Application 的 main 方法里我压根没有任何地方引用 HelloController 类,那么它的代码又是如何被服务器调用起来的呢?这就需要深入到 SpringApplication.run() 方法中看个究竟了。不过即使不看代码,我们也很容易有这样的猜想,SpringBoot 肯定是在某个地方扫描了当前的 package,将带有 RestController 注解的类作为 MVC 层的 Controller 自动注册进了 Tomcat Server。

还有一个让人不爽的地方是 SpringBoot 启动太慢了,一个简单的 Hello World 启动居然还需要长达 5 秒,要是再复杂一些的项目这样龟漫的启动速度那真是不好想象了。

再抱怨一下,这个简单的 HelloWorld 虽然 pom 里只配置了一个 maven 依赖,但是传递下去,它一共依赖了 36 个 jar 包,其中以 spring 开头的 jar 包有 15 个。说这是依赖地狱真一点不为过。

 

批评到这里就差不多了,下面就要正是进入主题了,看看 SpringBoot 的 main 方法到底是如何跑起来的。

SpringBoot 的堆栈

了解 SpringBoot 运行的最简单的方法就是看它的调用堆栈,下面这个启动调用堆栈还不是太深,我没什么可抱怨的。

 


 
public class TomcatServer { @Override public void start() throws WebServerException { ... } } 

接下来再看看运行时堆栈,看看一个 HTTP 请求的调用栈有多深。不看不知道一看吓了一大跳!

 

我通过将 IDE 窗口全屏化,并将其它的控制台窗口源码窗口统统最小化,总算勉强一个屏幕装下了整个调用堆栈。

不过转念一想,这也不怪 SpringBoot,绝大多数都是 Tomcat 的调用堆栈,跟 SpringBoot 相关的只有不到 10 层。

探索 ClassLoader

SpringBoot 还有一个特色的地方在于打包时它使用了 FatJar 技术将所有的依赖 jar 包一起放进了最终的 jar 包中的 BOOT-INF/lib 目录中,当前项目的 class 被统一放到了 BOOT-INF/classes 目录中。


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

这不同于我们平时经常使用的 maven shade 插件,将所有的依赖 jar 包中的 class 文件解包出来后再密密麻麻的塞进统一的 jar 包中。下面我们将 springboot 打包的 jar 包解压出来看看它的目录结构。


 
├── BOOT-INF │ ├── classes │ │ └── hello │ └── lib │ ├── classmate-1.3.4.jar │ ├── hibernate-validator-6.0.12.Final.jar │ ├── jackson-annotations-2.9.0.jar │ ├── jackson-core-2.9.6.jar │ ├── jackson-databind-2.9.6.jar │ ├── jackson-datatype-jdk8-2.9.6.jar │ ├── jackson-datatype-jsr310-2.9.6.jar │ ├── jackson-module-parameter-names-2.9.6.jar │ ├── javax.annotation-api-1.3.2.jar │ ├── jboss-logging-3.3.2.Final.jar │ ├── jul-to-slf4j-1.7.25.jar │ ├── log4j-api-2.10.0.jar │ ├── log4j-to-slf4j-2.10.0.jar │ ├── logback-classic-1.2.3.jar │ ├── logback-core-1.2.3.jar │ ├── slf4j-api-1.7.25.jar │ ├── snakeyaml-1.19.jar │ ├── spring-aop-5.0.9.RELEASE.jar │ ├── spring-beans-5.0.9.RELEASE.jar │ ├── spring-boot-2.0.5.RELEASE.jar │ ├── spring-boot-autoconfigure-2.0.5.RELEASE.jar │ ├── spring-boot-starter-2.0.5.RELEASE.jar │ ├── spring-boot-starter-json-2.0.5.RELEASE.jar │ ├── spring-boot-starter-logging-2.0.5.RELEASE.jar │ ├── spring-boot-starter-tomcat-2.0.5.RELEASE.jar │ ├── spring-boot-starter-web-2.0.5.RELEASE.jar │ ├── spring-context-5.0.9.RELEASE.jar │ ├── spring-core-5.0.9.RELEASE.jar │ ├── spring-expression-5.0.9.RELEASE.jar │ ├── spring-jcl-5.0.9.RELEASE.jar │ ├── spring-web-5.0.9.RELEASE.jar │ ├── spring-webmvc-5.0.9.RELEASE.jar │ ├── tomcat-embed-core-8.5.34.jar │ ├── tomcat-embed-el-8.5.34.jar │ ├── tomcat-embed-websocket-8.5.34.jar │ └── validation-api-2.0.1.Final.jar ├── META-INF │ ├── MANIFEST.MF │ └── maven │ └── org.springframework └── org └── springframework └── boot 

这种打包方式的优势在于最终的 jar 包结构很清晰,所有的依赖一目了然。如果使用 maven shade 会将所有的 class 文件混乱堆积在一起,是无法看清其中的依赖。而最终生成的 jar 包在体积上两也者几乎是相等的。

在运行机制上,使用 FatJar 技术运行程序是需要对 jar 包进行改造的,它还需要自定义自己的 ClassLoader 来加载 jar 包里面 lib 目录中嵌套的 jar 包中的类。我们可以对比一下两者的 MANIFEST 文件就可以看出明显差异


 
// Generated by Maven Shade Plugin Manifest-Version: 1.0 Implementation-Title: gs-spring-boot Implementation-Version: 0.1.0 Built-By: qianwp Implementation-Vendor-Id: org.springframework Created-By: Apache Maven 3.5.4 Build-Jdk: 1.8.0_191 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/gs-spring-boot Main-Class: hello.Application // Generated by SpringBootLoader Plugin Manifest-Version: 1.0 Implementation-Title: gs-spring-boot Implementation-Version: 0.1.0 Built-By: qianwp Implementation-Vendor-Id: org.springframework Spring-Boot-Version: 2.0.5.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: hello.Application Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Created-By: Apache Maven 3.5.4 Build-Jdk: 1.8.0_191 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/gs-spring-boot 

SpringBoot 将 jar 包中的 Main-Class 进行了替换,换成了 JarLauncher。还增加了一个 Start-Class 参数,这个参数对应的类才是真正的业务 main 方法入口。我们再看看这个 JarLaucher 具体干了什么

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。


 
public class JarLauncher{ ... static void main(String[] args) { new JarLauncher().launch(args); } protected void launch(String[] args) { try { JarFile.registerUrlProtocolHandler(); ClassLoader cl = createClassLoader(getClassPathArchives()); launch(args, getMainClass(), cl); } catch (Exception ex) { ex.printStackTrace(); System.exit(1); } } protected void launch(String[] args, String mcls, ClassLoader cl) { Runnable runner = createMainMethodRunner(mcls, args, cl); Thread runnerThread = new Thread(runner); runnerThread.setContextClassLoader(classLoader); runnerThread.setName(Thread.currentThread().getName()); runnerThread.start(); } } class MainMethodRunner { @Override public void run() { try { Thread th = Thread.currentThread(); ClassLoader cl = th.getContextClassLoader(); Class<?> mc = cl.loadClass(this.mainClassName); Method mm = mc.getDeclaredMethod("main", String[].class); if (mm == null) { throw new IllegalStateException(this.mainClassName + " does not have a main method"); } mm.invoke(null, new Object[] { this.args }); } catch (Exception ex) { ex.printStackTrace(); System.exit(1); } } } 

从源码中可以看出 JarLaucher 创建了一个特殊的 ClassLoader,然后由这个 ClassLoader 来另启一个单独的线程来加载 MainClass 并运行。

又一个问题来了,当 JVM 遇到一个不认识的类,BOOT-INF/lib 目录里又有那么多 jar 包,它是如何知道去哪个 jar 包里加载呢?我们继续看这个特别的 ClassLoader 的源码


 
class LaunchedURLClassLoader extends URLClassLoader { ... private Class<?> doLoadClass(String name) { if (this.rootClassLoader != null) { return this.rootClassLoader.loadClass(name); } findPackage(name); Class<?> cls = findClass(name); return cls; } } 

这里的 rootClassLoader 就是双亲委派模型里的 ExtensionClassLoader ,JVM 内置的类会优先使用它来加载。如果不是内置的就去查找这个类对应的 Package。


 
private void findPackage(final String name) { int lastDot = name.lastIndexOf('.'); if (lastDot != -1) { String packageName = name.substring(0, lastDot); if (getPackage(packageName) == null) { try { definePackage(name, packageName); } catch (Exception ex) { // Swallow and continue } } } } private final HashMap<String, Package> packages = new HashMap<>(); protected Package getPackage(String name) { Package pkg; synchronized (packages) { pkg = packages.get(name); } if (pkg == null) { if (parent != null) { pkg = parent.getPackage(name); } else { pkg = Package.getSystemPackage(name); } if (pkg != null) { synchronized (packages) { Package pkg2 = packages.get(name); if (pkg2 == null) { packages.put(name, pkg); } else { pkg = pkg2; } } } } return pkg; } private void definePackage(String name, String packageName) { String path = name.replace('.', '/').concat(".class"); for (URL url : getURLs()) { try { if (url.getContent() instanceof JarFile) { JarFile jf= (JarFile) url.getContent(); if (jf.getJarEntryData(path) != null && jf.getManifest() != null) { definePackage(packageName, jf.getManifest(), url); return null; } } } catch (IOException ex) { // Ignore } } return null; } 

ClassLoader 会在本地缓存包名和 jar包路径的映射关系,如果缓存中找不到对应的包名,就必须去 jar 包中挨个遍历搜寻,这个就比较缓慢了。不过同一个包名只会搜寻一次,下一次就可以直接从缓存中得到对应的内嵌 jar 包路径。

深层 jar 包的内嵌 class 的 URL 路径长下面这样,使用感叹号 ! 分割


 
jar:file:/workspace/springboot-demo/target/application.jar!/BOOT-INF/lib/snakeyaml-1.19.jar!/org/yaml/snakeyaml/Yaml.class 

不过这个定制的 ClassLoader 只会用于打包运行时,在 IDE 开发环境中 main 方法还是直接使用系统类加载器加载运行的。

不得不说,SpringbootLoader 的设计还是很有意思的,它本身很轻量级,代码逻辑很独立没有其它依赖,它也是 SpringBoot 值得欣赏的点之一。

HelloController 自动注册

还剩下最后一个问题,那就是 HelloController 没有被代码引用,它是如何注册到 Tomcat 服务中去的?它靠的是注解传递机制。

 

SpringBoot 深度依赖注解来完成配置的自动装配工作,它自己发明了几十个注解,确实严重增加了开发者的心智负担,你需要仔细阅读文档才能知道它是用来干嘛的。Java 注解的形式和功能是分离的,它不同于 Python 的装饰器是功能性的,Java 的注解就好比代码注释,本身只有属性,没有逻辑,注解相应的功能由散落在其它地方的代码来完成,需要分析被注解的类结构才可以得到相应注解的属性。

那注解是又是如何传递的呢?


 
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } @ComponentScan public @interface SpringBootApplication { ... } public @interface ComponentScan { String[] basePackages() default {}; } 

首先 main 方法可以看到的注解是 SpringBootApplication,这个注解又是由ComponentScan 注解来定义的,ComponentScan 注解会定义一个被扫描的包名称,如果没有显示定义那就是当前的包路径。SpringBoot 在遇到 ComponentScan 注解时会扫描对应包路径下面的所有 Class,根据这些 Class 上标注的其它注解继续进行后续处理。当它扫到 HelloController 类时发现它标注了 RestController 注解。


 
@RestController public class HelloController { ... } @Controller public @interface RestController { } 

而 RestController 注解又标注了 Controller 注解。SpringBoot 对 Controller 注解进行了特殊处理,它会将 Controller 注解的类当成 URL 处理器注册到 Servlet 的请求处理器中,在创建 Tomcat Server 时,会将请求处理器传递进去。HelloController 就是如此被自动装配进 Tomcat 的。

扫描处理注解是一个非常繁琐肮脏的活计,特别是这种用注解来注解注解(绕口)的高级使用方法,这种方法要少用慎用。SpringBoot 中有大量的注解相关代码,企图理解这些代码是乏味无趣的没有必要的,它只会把你的本来清醒的脑袋搞晕。SpringBoot 对于习惯使用的同学来说它是非常方便的,但是其内部实现代码不要轻易模仿,那绝对算不上模范 Java 代码。

 

最后表示自己真的很讨厌 SpringBoot 这只怪兽,但是很无奈,这个世界人人都在使用它。这就好比老人们常常告诫年轻人的那句话:如果你改变不了世界,那就先适应这个世界吧!

欢迎工作一到八年的Java工程师朋友们加入Java高级交流:854630135

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢

linux CentOS7安装VCS、Verdi、SCL 1.Linux下所需安装文件 链接:https://pan.baidu.com/s/1E_l4ARFYEnp57jlX95Bpog 提取码:ggdz ​ synopsysinstaller_v5.0 ​ scl_v2018.06 ​ vcs_mx_vO-2018.09-SP2 ​ vcs_vO-2018.09-SP2 ​ verdi-2018.9 2.Windows破解文件 ​ scl_keygen_2030 3.安装installer 打开终端,进入synopsysinstaller_ 阅读详情

相关推荐

tewa-707e光猫超级密码获取方法

这款光猫很魔性,没有WIFI,没有USB接口,没有Telnet,试了很多方法去获取超级密码都不行。解密时可能会报错,可能是文件后面部分不晓得是咋拼接过来的,用AES解密时分组数据有问题。3、这个文件是用AES加密了的,直接打开是乱码,所以得用AES解密,key为16个字节的 x00。打开解出来的文件,然后搜索 telecomadmin,就可以发现超级密码了。参考资料里面的光猫不是同一款,但解密的方法基本相同的。

shaowl111的专栏 1万+

Redis实现分布式锁

分布式锁及实现方案

4万+

微信好友恢复全攻略(2025年3月更新)

适用于仅被拉黑场景(成功率100%)针对7天内误删场景(成功率78%)

2301_77933942的博客 1万+

Spring Boot引起的“堆外内存泄漏”排查及经验总结

背景为了更好地实现对项目的管理,我们将组内一个项目迁移到MDP框架(基于Spring Boot),随后我们就发现系统会频繁报出Swap区域使用量过高的异常。笔者被叫去帮忙查看原因,发现配置了4G堆内内存,但是实际使用的物理内存竟然高达7G,确实不正常。JVM参数配置是“-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+AlwaysPreTo...

weixin_30274627的博客 623

S32K144 SDK实战:FTM输入捕获模块的配置与应用

本文详细介绍了S32K144 SDK中FTM输入捕获模块的配置与应用实践。从基础认知到开发环境搭建,再到引脚配置和中断处理技巧,全面解析了如何利用FTM模块实现高精度信号测量。特别适用于电机控制、工业测速等场景,帮助开发者快速掌握S32K144的FTM输入捕获功能。

weixin_42566424的博客 217

ArcGIS标注(二分式、三分式、条件分类标注、上下标、牵引线、文字格式化…)

ArcGIS中的各种标注方法、可能遇到的问题和细节。

m0_73306837的博客 5276

Redis(九) - Redis之分布式锁

Redis(九) - Redis之分布式锁

及时当勉励,岁月不待人 1万+

Multisim电路仿真与实验:包含连续时间系统的模拟、周期信号频谱分析、组合逻辑电路:竞争冒险、数字锁

这些实际系统可以是电的或非电的物理量系统,也可以是经济、军事等非物理量系统。矩形周期信号的幅度和周期保持不变,随着占空比的增加(即脉宽加大),主峰高度越高,主峰宽度越窄,各谱线间隔越窄,主峰包含的谱线数量越少,有效频带宽度越窄,主峰内高次谐波分量越少。输入信号所经路径不同而引起的冒险现象,按短暂尖峰极性的不同,可将冒险分为“0-1-0”型险象(也称“1”型)和“1-0-1”型险象(也称“0”型)理想的正弦波的幅度谱包含1条谱线,证明其只有基频分量,而无谐波分量,如果能测出谐波分量,说明该正弦波已有失真。

Jumpingtigger_35的博客 1万+

什么是分布式锁?几种分布式锁分别是怎么实现的?

一、什么是分布式锁: 1、什么是分布式锁: 分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。 2、分布式锁应该具备哪些条件: 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行 高可用的获取锁与释放锁 高性能的获取锁与释放锁 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误) ...

m0_67645544的博客 6万+

从GitHub到海浪模拟:手把手教你配置WAVEWATCHⅢ 6.07.0的完整开发与测试环境

本文详细指导如何在Ubuntu系统中配置WAVEWATCHⅢ 6.07.0的开发与测试环境,涵盖虚拟机设置、依赖安装、源码编译及案例验证的全流程。通过步骤化教程,帮助科研人员快速搭建高效的海浪模拟平台,提升海洋数值模拟的研究效率。

weixin_42568801的博客 326

数据服务:数据服务框架Magic-API 介绍及实践

接口快速开发框架 基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller、Service、Dao、Mapper、XML、VO等Java对象即可完成常见的HTTP API接口开发 。在线页面快速开发平台 基于 vue 的在线页面快速开发平台,主要把所有的 vue 代码都改为在线配置,最终获得一个 json 格式的页面新一代爬虫平台 基于Java实现的新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。今天给大家安利一

GIS摆渡人 2063

SpringBoot 究竟是如何跑起来的

SpringBoot 究竟是如何跑起来的?看完这篇文章让你恍然大悟!!

万维网连五洲,二进制写尽天下事,喜欢结识新伙伴寻找志同道合的朋友,欢迎一起探讨学习 3257

2026华为OD面试题084:贪心的商人

商人经营一家店铺,有 `number` 种商品。由于仓库限制,每件商品的最大持有数量是 `item[index]`。 每种商品每天有一个价格 `item_price[item_index][day]`。通过对商品买进和卖出获取利润,请给出商人在 `days` 天内能获取到的最大利润。 注:同一件商品可以反复买进和卖出。 输入描述: - 第一行输入商品数量 `number` - 第二行输入商人售货天数 `days` - 第三行输入 `number` 个整数,表示每件商品的最大持有数量 `item[in

琅嬛福地 276

五分钟了解如何把Springboot+Vue跑起来

Spring Boot核心架构解析:本文介绍了Spring Boot项目的标准目录结构与环境配置要点,重点解析了四大核心组件:1)Entity层通过JPA注解实现对象关系映射;2)Repository层提供数据访问抽象;3)Service层封装业务逻辑;4)Controller层处理HTTP请求。内容涵盖设计理念、核心注解、代码示例及最佳实践,如事务管理、分层设计、DTO转换等,并提供了application.properties配置示例。开发时需注意环境依赖、接口隔离、业务分层等关键点,确保项目可维护性和

2301_79970509的博客 812

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring Boot是如何跑起来的

不得不说 SpringBoot 太杂乱了,我原本只想研讨一下 SpringBoot 最简略的 HelloWorld 程序是如何从 main 办法一步一步跑起来的,可是这却是一个适当深的坑。你可以试着沿着调用栈代码一层一层的深入进去,假如你不打断点,你根本不知道接下来程序会往哪里活动。这个不同于我研讨过去的 Go 言语、Python 言语结构,它们一般都十分直接了当,规划上清晰易懂,代码写起来简略,...

zxl2016的博客 660

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

SpringBoot 究竟是如何跑起来的?

作者简介:钱文品(老钱),互联网分布式高并发技术十年老兵,目前任掌阅科技资深后端工程师。熟练使用 Java、Python、Golang 等多种计算机语言,开发过游戏,制作过网站,写过消息推送系统和MySQL 中间件,实现过开源的 ORM 框架、Web 框架、RPC 框架等不得不说 SpringBoot 太复杂了,我本来只想研究一下 SpringBoot 最简单的 HelloWorld 程序是如何从...

weixin_33753003的博客 120

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Spring Boot(3) SpringBoot是怎么跑起来的(1)

1、今天稍微学习了一下Spring Boot的运行机制,在这里把我收获到的知识分享出来,是对Spring Boot运行机制的一点点个人的看法。 2、大家看到SpringBoot项目的时候最大的疑惑应该是为什么本来SSM里的那些xml配置文件都不见了呢,其实Spring Boot是对现在许多主流的第三方框架进行了整合,我们可以在一个空项目的pom文件中看到下边这样的配置,其实仔细研究一下就可以看到...

weixin_39452731的博客 321

springboot 扫描jar包中bean_Spring Boot这只怪物到底是如何跑起来的?

作者:钱文品 |《Redis 深度历险:核心原理与应用实践》作者微信公众号:码洞(ID:codehole)不得不说 SpringBoot 太复杂了,我本来只想研究一下 SpringBoot 最简单的 HelloWorld 程序是如何从 main 方法一步一步跑起来的,但是这却是一个相当深的坑。你可以试着沿着调用栈代码一层一层的深入进去,如果你不打断点,你根本不知道接下来程序会往哪里流动。...

weixin_39817391的博客 390
上一篇: 面试官问我,使用Dubbo有没有遇到一些坑?我笑了。
下一篇: 从构建分布式秒杀系统聊聊验证码
javaQQ群 866865133
博客等级 码龄8年 59粉丝 309原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值