Spring Boot这只怪物到底是如何跑起来的?

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

经过了一番痛苦的摧残,我还是把 SpringBoot 的运转原理摸清楚了,这儿分享给大家。

一、Hello World

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

当我打开浏览器看到服务器正常地将输出呈现在浏览器的时分,我不由大喊 —— 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 运转的最简略的办法就是看它的调用仓库,下面这个发动调用仓库还不是太深,我没什么可诉苦的。

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

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

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

三、探究 ClassLoader

SpringBoot 还有一个特色的当地在于打包时它运用了 FatJar 技能将一切的依靠 jar 包一同放进了终究的 jar 包中的 BOOT-INF/lib 目录中,当时项目的 class 被一致放到了 BOOT-INF/classes 目录中。

这不同于咱们平常经常运用的 maven shade 插件,将一切的依靠 jar 包中的 class 文件解包出来后再密密麻麻的塞进一致的 jar 包中。下面咱们将 springboot 打包的 jar 包解压出来看看它的目录结构。

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

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

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

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

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

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

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 的注解就比如代码注释,自身只要特点,没有逻辑,注解相应的功用由散落在其它当地的代码来完结,需求剖析被注解的类结构才可以得到相应注解的特点。

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

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

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

扫描处理注解是一个十分繁琐龌龊的活计,特别是这种用注解来注解注解(绕口)的高级运用办法,这种办法要少用慎用。SpringBoot 中有很多的注解相关代码,企图理解这些代码是乏味无趣的没有必要的,它只会把你的原本清醒的脑袋搞晕。SpringBoot 对于习气运用的同学来说它是十分便利的,可是其内部完成代码不要轻易模仿,那绝对算不上榜样 Java 代码。

最后老钱表明自己真的很厌烦 SpringBoot 这只怪兽,可是很无法,这个世界人人都在运用它。这就比如老人们常常告诫年轻人的那句话:假如你改变不了世界,那就先习惯这个世界吧!

Spring Boot这只怪物到底是如何跑起来的?相关推荐

  1. Spring Boot启动过程源码分析--转

    https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...

  2. java集合系列之18 spring boot程序员的必修课

    Spring Boot 2.0 的推出又激起了一阵学习 Spring Boot 热,就单从我个人的博客的访问量大幅增加就可以感受到大家对学习 Spring Boot 的热情,那么在这么多人热衷于学习 ...

  3. Spring Boot面试题(2020最新版)

    转载自  Spring Boot面试题(2020最新版) 概述 什么是 Spring Boot? Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主 ...

  4. 通过OAuth 2.0和Okta使用安全的服务器到服务器通信构建Spring Boot应用

    "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 大多数O ...

  5. 通过OAuth 2.0和Okta构建具有安全的服务器到服务器通信的Spring Boot应用

    "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 大多数O ...

  6. 为什么说Java 程序员必须掌握 Spring Boot?

    大部分人选择学习Java,自然是因为看上了Java的薪资待遇,java的地位可谓在编程界属于大佬,最近Spring Boot 2.0 的推出来后,又激起了一阵学习 Spring Boot 的热潮,那么 ...

  7. 松哥整理了 15 道 Spring Boot 高频面试题,看完当面霸

    什么是面霸?就是在面试中,神挡杀神佛挡杀佛,见招拆招,面到面试官自惭形秽自叹不如!松哥希望本文能成为你面霸路上的垫脚石! 做 Java 开发,没有人敢小觑 Spring Boot 的重要性,现在出去面 ...

  8. Spring Boot学习总结(16)——为什么说Java程序员到了必须掌握Spring boot的时候了?

    分享一个大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!点击浏览教程 Spring Boot 2.0 的推出又激起了一阵学习 Spring Boot 热, ...

  9. Spring Boot四大神器之Auto Configuration

    Spring Boot非常简单容易上手,它隐藏了很多内容而不需要你去关心.但对于一个好的开发人员也许希望知道Spring Boot自动配置背后到底发生了什么? Spring Boot并不属于一种新的技 ...

最新文章

  1. Spring中的时间调度,定时任务
  2. mscoreei.dll没有被指定在windows上运行_在Windows上使用Docker运行.NetCore
  3. UNIX再学习 -- 可重入函数和 SIGCHLD 语义
  4. Ibatis的类型处理器TypeHandler解析
  5. 别人的20几岁 vs 互联网人的20几岁
  6. 女程序员怀孕7个月坚持上班敲代码
  7. 通过例子理解事务的4种隔离级别
  8. 【报告分享】90后人群消费趋势研究报告.pdf
  9. 使用DataBinding的Android SearchView示例教程
  10. C++_实现一个简单的智能指针shared_ptr
  11. 搜索引擎提交软件_网站如何被搜索引擎快速收录?
  12. mtk刷机报错4032专业维修教程(图文)
  13. ROS的激光雷达、 加速度计、 陀螺仪传感器
  14. 大学教师与大学学生的彼此期望
  15. 中科大网上财务报销填写流程
  16. 单例模式只会懒汉饿汉?读完本篇让你面试疯狂加分
  17. cmake使用boost库
  18. linux下,代码阅读工具,understand
  19. 更改laravel的默认端口8000
  20. 动态规划设计方法详解最长递增子序列

热门文章

  1. 【STM32】FLASH擦写+FLASH相关操作+注意事项
  2. ajax jsonjar包,json-lib.jar
  3. 报送数据标准校验java_Java:数据校验 - osc_gaqp1a2z的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. 常见网络命令整理(ping、trcert、netstat)
  5. java并发LockSupport
  6. Redis所需内存 超过可用内存怎么办
  7. Centos/linux开放端口
  8. 利用 Chrome 原生功能截图网页全图
  9. 剑指offer(21)栈的压入、弹出序列
  10. MySQL CAST与CONVERT 函数的用法