【Spring】原来SpringBoot是这样玩的
菜瓜:我自己去调Mvc的源码差点没给Spring的逻辑秀死。。。难受
水稻:那今天咱们看一个简单易用的SpringBoot吧
菜瓜:可以,这个我熟悉
水稻:熟悉?
菜瓜:当我没说,请开始你的表演
水稻:我没有别的意思,就是单纯的反问(手动狗头)。平时工作中用多了SpringBoot。咱们今天带着几个问题来看看它的操作吧
- 如何启动Spring容器
- 如何内嵌Tomcat容器
- 如何完成自动装配,就是0配置
菜瓜:你确定这是我熟悉的SpringBoot???
水稻:。。。看过来
- 启动类点进去
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
...
listeners.starting();
try {
...
// ①创建Spring上下文容器对象 - 默认Servlet容器
context = createApplicationContext();
...
// ②调用refresh方法 - 回到熟悉的容器启动流程
refreshContext(context);
afterRefresh(context, applicationArguments);
...
...
return context;
}- ① 创建上下文对象
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
...
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
- ②启动容器
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
...
// ①springboot 内嵌tomcat容器
onRefresh();
... } @Override
protected void onRefresh() {
super.onRefresh();
try {
// ②创建Servlet容器 默认tomcat
createWebServer();
}
...
} private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// ③看进去 回到mvc集成tomcat的场景
this.webServer = factory.getWebServer(getSelfInitializer());
}
...
initPropertySources();
} @Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
水稻:好了,第一步和第二步完成了
菜瓜:就这???
水稻:是不是极其简单,令人发指。重头戏是后面的自动装配
- 回到咱们启动类的注解上
...
// 标记自身被扫描
@SpringBootConfiguration
// 下一步 - 自动装配入口
@EnableAutoConfiguration
// 扫描bean路径 - 约定是启动类所在的包:所以没事别把启动类挪走(都是泪)
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication ->
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration -> 重要
public class AutoConfigurationImportSelector ... {
@Override
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
...
// 获取以EnableAutoConfiguration命名的/META-INF/Spring.factories文件中的value去重
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
// 启动的时候断点可以看到
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}AutoConfigurationImportSelector 中的process是被ConfigurationClassPostProcessor通过processConfigBeanDefinitions方法调用(调用链如下)
1. this.processConfigBeanDefinitions(registry);
2. parser.parse(candidates);
3. this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
4. sourceClass = this.doProcessConfigurationClass(configClass, sourceClass);
5. this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
6. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
7. handler.processGroupImports();
8. grouping.getImports().forEach...
9. this.group.process(...);
--
搜集到需要自动装配的类,封装成BeanDefinition后续实例化,实现自动装配功能
譬如引入WebMvcAutoConfiguration类 - webmvc功能自动集成
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
菜瓜:原来如此。你把调用链拎出来就简单了很多。自动装配就是通过SPI加载org.springframework.boot.autoconfigure包下的class,封装成BeanDefinition后交给容器加载
总结:SpringBoot只需要一行代码便能启动一个Java应用。完全解放开发者复杂的配置
- 内嵌Servlet容器,默认tomcat
- 启动SpringWeb容器
- 自动装配了简单web应用需要的工具和组建
【Spring】原来SpringBoot是这样玩的的更多相关文章
- Spring及SpringBoot @Async配置步骤及注意事项
前言 最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者.考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了.但 ...
- spring springMvc spring-boot spring-cloud分别是什么
本文来源于:克己习礼成仁 的<spring springMvc spring-boot spring-cloud分别是什么> 什么是spring 关于spring的定义无论是从官方还是 ...
- Spring和SpringBoot比较,解惑区别
1.概述: 对于Spring和SpringBoot到底有什么区别,我听到了很多答案,刚开始迈入学习SpringBoot的我当时也是一头雾水,随着经验的积累.我慢慢理解了这两个框架到底有什么区别,我相信 ...
- spring 或 springboot统一异常处理
spring 或 springboot统一异常处理https://blog.csdn.net/xzmeasy/article/details/76150370 一,本文介绍spring MVC的自定义 ...
- 品Spring:SpringBoot和Spring到底有没有本质的不同?
现在的Spring相关开发都是基于SpringBoot的. 最后在打包时可以把所有依赖的jar包都打进去,构成一个独立的可执行的jar包.如下图13: 使用java -jar命令就可以运行这个独立的j ...
- 品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”
上一篇文章强调了bean定义注册占Spring应用的半壁江山.而且详细介绍了两个重量级的注册bean定义的类. 今天就以SpringBoot为例,来看看整个SpringBoot应用的bean定义是如何 ...
- 品Spring:SpringBoot发起bean定义注册的“二次攻坚战”
上一篇文章整体非常轻松,因为在容器启动前,只注册了一个bean定义,就是SpringBoot的主类. OK,今天接着从容器的启动入手,找出剩余所有的bean定义的注册过程. 具体细节肯定会颇为复杂,同 ...
- Spring SpringMVC SpringBoot SpringCloud 注解整理大全
Spring SpringMVC SpringBoot SpringCloud 注解整理 才开的博客所以放了一篇以前整理的文档,如果有需要添加修改的地方欢迎指正,我会修改的φ(๑˃∀˂๑)♪ Spri ...
- Spring 与 SpringBoot 的区别
概述 Spring 与 SpringBoot 有什么区别???梳理一下 Spring 和 SpringBoot 到底有什么区别,从 Spring 和 SpringBoot 两方面入手. Spring ...
随机推荐
- JavaSE(二) 关键字、标识符、注释
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1关键字与标识符 1.1 java关键字的使用 定义:被Java语言赋予了特殊含义,用做专门用途的字符串 ...
- Java实现 LeetCode 546 移除盒子(递归,vivo秋招)
546. 移除盒子 给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色. 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止.每一轮你可以移除具有相同颜色的连续 k 个盒子( ...
- Java实现 LeetCode 374 猜数字大小 II
375. 猜数字大小 II 我们正在玩一个猜数游戏,游戏规则如下: 我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字. 每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了. 然而,当你猜 ...
- Java实现 LeetCode 357 计算各个位数不同的数字个数
357. 计算各个位数不同的数字个数 给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n . 示例: 输入: 2 输出: 91 解释: 答案应为除去 11, ...
- Java实现 LeetCode 329 矩阵中的最长递增路径
329. 矩阵中的最长递增路径 给定一个整数矩阵,找出最长递增路径的长度. 对于每个单元格,你可以往上,下,左,右四个方向移动. 你不能在对角线方向上移动或移动到边界外(即不允许环绕). 示例 1: ...
- Java实现蓝桥杯算法提高12-2扑克排序
扑克牌排序 问题描述 扑克牌排序:构造扑克牌数组,对扑克牌进行排序. 排序原则如下:数字从小到大是2-10.J.Q.K和A,花色从小到大是方块(diamond).梅花(club).红桃(heart). ...
- Java实现 黑洞数
任意一个5位数,比如:34256,把它的各位数字打乱,重新排列,可以得到一个最大的数:65432,一个最小的数23456.求这两个数字的差,得:41976,把这个数字再次重复上述过程(如果不足5位,则 ...
- CSS清除浮动&内容居中&文字溢出
学习! 1.CSS清除浮动的方法 (1)添加标签清除浮动: 在浮动元素结尾处,并列的添加标签<div style="clear:both;"></div>. ...
- js-ajax方法详解以及封装
本文主要从使用ajax请求的步骤.ajax状态码和http响应状态码以及ajax封装三个方面阐述 一.使用ajax请求的步骤 // 一.创建 XMLHttpRequest 对象 var xhr = n ...
- Volley框架学习资料汇总
Android Volley完全解析(一),初识Volley的基本用法(http://blog.csdn.net/guolin_blog/article/details/17482095/)