现在的 Spring 相关开发都是基于 SpringBoot 的.
最后在打包时可以把所有依赖的 jar 包都打进去, 构成一个独立的可执行的 jar 包. 如下图 13:
使用 java -jar 命令就可以运行这个独立的 jar 包. 如下图 14:
这个 jar 包的执行入口就是一个 main 函数, 典型的格式如下:
- @SpringBootApplication
- public class TasteSpringApplication {
- public static void main(String[] args) {
- SpringApplication.run(TasteSpringApplication.class, args);
- }
- }
从代码中可以得知, SpringApplication 这个类就是 SpringBoot 的总入口.
以上这些内容, 早已是路人皆知的事情了, 这里只是再赘述一遍.
进入 SpringApplication 这个类的源码里, 首先看到的就是几个 application context 所使用的类.
首先是 AnnotationConfigApplicationContext, 这是基于注解的非 web 应用使用的类, 它是 spring-context 里面的类, 现在也用于 SpringBoot 中.
这表明对于非 Web 应用来说, 采用传统的 Spring 构建, 或是采用现在的 SpringBoot 构建, 核心部分并没有什么本质区别. 毕竟连类都是用的同一个.
再看就是 AnnotationConfigServletWebServerApplicationContext, 这是基于注解的 Web 应用使用的类, 注意, 这个类是 SpringBoot 里的类.
其实大家都知道, 在还没有 SpringBoot 时, 基于传统 Spring 构建 Web 应用时使用的是 AnnotationConfigWebApplicationContext 这个类.
这个类位于 spring-Web 中, 显然它是 Spring 里面的类.(注: 本文中所说的 Spring 指的是 SpringFramework)
这里有一个问题, 不知你是否发现, 从 Spring 到 SpringBoot, 非 Web 应用使用的类没有变, Web 应用使用的类改变了, 为啥呢?
这个问题其实很简单, 从它们的启动方式的差异就能很好的说明.
传统 Spring 构建的 Web 应用, 会打成一个 war 包, 放入 tomcat 下面.
先启动 tomcat, 然后 tomcat 再去加载它下面的 Web 应用(即 war 包).
SpringBoot 构建的 Web 应用, 会打成一个 jar 包, 采用内嵌的 tomcat.
先启动 jar 包, 会进入 SpringBoot 中, 然后再去启动 tomcat.
因为现在 SpringBoot 要来负责启动和停止 Web server, 这和传统 Spring 完全不同, 所以它要自己实现一个 Web application context 所使用的类.
由此我们可以推断出, 这个类里一定有关于 Web server 启动和停止的相关内容.
再来观察一个细节, 没错, 就是类名称.
传统 Spring 使用的类名称可以提炼出一个关键词, 就是 Web.SpringBoot 使用的类名称可以提炼出的关键词是 ServletWebServer.
前者只有 Web, 说明只关注 Web 的问题, 后者除了 Web 外还有 Server, 说明除了关注 Web 外还要关注服务器, 即 tomcat,jetty 等这些 Web 服务器.
由此可见, 从类名称上的解释和刚刚从启动方式上的解释是吻合的, 是一致的.
这也说明,"时刻关注细节, 你将发现更多". 这句话不仅可以用在工作当中, 亦可以用在学习中, 生活中.
细心的同学又会发现, 后者中还有一个 Servlet 呢, 这又怎么解释呢?
这说明这个 WebServer 是基于 Servlet 实现的. 难道还有不是基于 Servlet 的吗? 有啊, 那就是基于 Reactive(响应式或反应式)的.
响应式使用的类是这个 AnnotationConfigReactiveWebServerApplicationContext. 可以仔细对比一下名字.
Spring 从 5.x 引入了响应式编程. 这里不做深入讨论, 需要的话可以去看 "编程新说" 这个号之前的文章.
接着我们去源码里看看, 来证实一下我们的猜想. 进入 ServletWebServerApplicationContext 类, 就是刚刚那个类的父类.
首先它定义了一个 WebServer, 如下图 01:
其次又创建了这个 WebServer, 如下图 02:
接着又启动了这个 WebServer, 如下图 03:
最后又关闭和释放了这个 WebServer, 如下图 04:
由此证明了我们的猜想, 确实有关于 Web 服务器的 "全套" 操作.
现在 SpringBoot 翻身成了主人, 它不仅可以启停 Web 服务器, 还可以选择 Web 服务器, 是用 tomcat,jetty 还是 netty, 都是可以配置的. 爽吧.
Spring 的核心就是 IoC 容器, 容器所作的事情就是 bean 定义的注册, bean 的实例化, 初始化, 依赖的装配, bean 方法的调用, bean 实例的销毁.
我们先来看看 bean 定义的注册吧.
首先看下传统 Spring 的, 也就是 AnnotationConfigWebApplicationContext 这个类的.
先定义两个成员变量, 存储要注册的类和要扫描的包, 如下图 05:
然后又用两个类 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 来负责注册类和扫描包, 如下图 0607:
最后就是具体的来执行注册类和扫描包, 如下图 08:
接着再看下 SpringBoot 的, 也就是 AnnotationConfigServletWebServerApplicationContext 这个类的.
也是先定义两个成员变量, 和上面的如出一辙, 如下图 09:
也是用相同的两个类来负责处理, 如下图 1011:
编程新说注: 实例化时虽然调用的构造函数不同, 但是最终执行的却是相同的构造函数.
最终执行具体的处理也是相同的, 如下图 12:
由此可以看出传统 Spring 和 SpringBoot 在对待 bean 定义注册这一块, 完全相同, 没有任何区别.
其实这很好理解, IoC 容器这块内容在 Spring 中已经发展的相当成熟了, 是不会再有人轻易去修改它了.
因此 SpringBoot 和 Spring 在本质上没什么差别, 注意这里说的是本质.
由于 SpringBoot 的启动方式是把自身提前把 Web 服务器移后(即采用内嵌 Web 服务器), 所以这块是额外新增的内容.
由于 SpringBoot 采用根据条件 (condition) 自动配置的方式(AutoConfiguration), 所以这块也是额外新增的内容.
这两块都是额外新增的内容, 和传统 Spring 基本没啥关系.
因此在 SpringBoot 和 Spring 重叠的部分, 其实本质没啥区别.
(END)
>>> 品 Spring 系列文章 <<<
品 Spring: 帝国的基石
品 Spring:bean 定义上梁山
品 Spring: 实现 bean 定义时采用的 "先进生产力"
品 Spring: 注解终于 "成功上位"
品 Spring: 能工巧匠们对注解的 "加持"
作者是工作超过 10 年的码农, 现在任架构师. 喜欢研究技术, 崇尚简单快乐. 追求以通俗易懂的语言解说技术, 希望所有的读者都能看懂并记住. 下面是公众号和知识星球的二维码, 欢迎关注!
来源: https://www.cnblogs.com/lixinjie/p/taste-spring-006.html