SpringBoot相关原理

静态资源配置原理

SpringBoot启动默认加载 很多xxxAutoConfiguration 类(自动配置类)。以SpringMVC功能的自动配置类 WebMvcAutoConfiguration为例。其使用注解 @EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })将属性与配置类中的值进行绑定。处理资源的默认规则由 addResourceHandlers()规定。若resources.addMappings=false会直接返回(禁用所有静态资源的访问)。之后会在Spring设置的默认的路径(”classpath:/META-INF/resources/”,”classpath:/resources/”, “classpath:/static/”, “classpath:/public/”)下寻找静态资源。

Rest原理(表单提交)

在我们没有自定义HiddenHttpMethodFilter的时候会使用默认值进行处理。表单提交时再请求方式是POST的前提下使用 _method=PUT/DELETE时。请求会被HiddenHttpMethodFilter拦截。判断是POST请求之后获取 _method的值。 原生的request会被包装为requestWrapper,requestWrapper重写了getMethod方法,返回的是_method对应的值。

请求映射原理

主要起作用的是 DispatcherServlet中的 doDispatch()方法。在获取到请求之后 getHandler()会根据请求找到合适的Handler(即Controller的方法)。Spring在启动时扫描所有的Controller并解析注解,并把这些信息保存到 HandlerMappings中,其中 RequestMappingHandlerMapping保存了所有的@RequestMapping 和handler的映射规则。根据这些映射规则以及传入的 request最终确定使用哪个Controller方法

参数处理原理

首先在HandlerMapping中找到可以处理请求的Handler(即Controller的方法)。然后为当前的Handler寻找一个适配器( HandlerAdapter)。之后会执行适配器的 handle()方法( mv = ha.handle(processedRequest, response, mappedHandler.getHandler());)。在这个方法中会获得参数解析器 argumentResolvers。参数解析器会根据注解的类型来解析其对应的参数。SpringMVC能解析多少种方法参数,取决于有多少种参数解析器。在获得参数解析器后,也会获得返回值处理器 returnValueHandlers,返回值处理器决定了能返回哪些参数。真正执行目标方法的函数是ServletInvocableHandlerMethod类中的 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);。确定目标方法每一个参数的值使用到了如下函数: Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);,首先获取到方法参数的详细信息,通过增强for循环挨个判断使用哪个参数解析器,如果匹配成功,就将解析器放入缓存,以提高后续程序的执行效率。然后使用解析器进行解析得到参数对应的值。然后遍历所有的方法参数获得所有参数对应的值。

会在参数解析的过程中使用 WebDataBinder将请求参数的值绑定到指定的JavaBean里。因为使用HTTP协议传输的数据,传输的都是文本,而我们需要具体的包装类,例如Integer,Date等等,WebDataBinder会利用里面的 Converters将请求数据转化成指定的数据类型。再次封装到JavaBean中。

如果传输的数据和自己封装的类的属性不是一一对应匹配的。可以通过重写 WebMvcConfigurer中的 addFormatters方法,再重写addFormatters中的 convert方法,自己按需将请求域中的数据封装到自己的自定义类中。

内容协商原理

内容协商:根据客户端接收能力不同,返回不同类型的数据。内容协商是在获取了请求参数之后的事情。其需要将返回值进行处理,转化为客户端能够接收的数据类型。

在拿到对应注解的处理器之后,在核心方法 writeWithMessageConverters()中,首先获取客户端支持接收的内容类型(默认获取Accept请求头字段的内容)。然后拿到能够接收的类型和能够提供的类型。使用双重for循环进行匹配得到最佳匹配类型。然后再是一个for循环,在 messageConverters中寻找支持最佳匹配类型的 Convert,之后再使用这个Convert进行数据类型的转换。

视图解析原理

在处理的过程中,所有传入的数据和视图地址都会被放在 ModelAndViewContainer中。在调用处理器方法执行完之后会返回一个 ModelAndView对象。之后由 processDispatchResult()方法决定页面如何响应。在processDispatchResult()中由 render()方法进行页面渲染。render()方法通过视图解析器解析视图名称得到 View对象。View对象再调用自己的 render()方法进行页面渲染。

拦截器原理

根据当前请求,找到可以处理请求的 handler以及handler的所有拦截器,并把拦截器放在 HandlerExecutionChain中。然后为当前handler找一个适配器( HandlerAdapter),在适配器解析前执行 applyPreHandle()。在该方法先 顺序执行所有拦截器的 preHandle()方法,如果返回为True,执行下一个拦截器的preHandle(),如果返回为False,则 倒序执行所有已经执行了的拦截器的 afterCompletion()方法,并直接跳出,不执行目标方法。

所有的拦截器返回都为True,之后会执行目标方法。之后会 倒序执行所有拦截器的 postHandle()方法。在前面的步骤中有任何的异常,都会直接 倒序执行afterCompletion()方法。

在页面成功渲染完成之后,也会 倒序执行afterCompletion()方法。

文件上传原理

文件上传解析器先使用 isMultipart()先判断是否为文件上传请求,之后使用 resolveMultipart()对请求进行解析,并封装为 MultipartHttpServletRequest对象。之后寻找一个是适配器( HandlerAdapter),再执行适配器的 handle()方法,在 handle()方法中会使用参数解析器 argumentResolvers中的 RequestPartMethodArgumentResolver对属于文件类型的参数进行解析,将request中的文件信息封装为一个 Map,key是自己定义的名字,value是封装好的 MultipartFile。之后返回 ModelAndView,之后再进行页面渲染。

SpringBoot启动过程

首先传入启动类的class文件

  • 根据传入的class文件创建SpringApplication
  • WebApplicationType.deduceFromClasspath();判断当前的web应用类型是 SERVLET还是 REACTIVE
  • 使用 getSpringFactoriesInstances(xxx.class)给注册初始化器传值,其实是在spring.factories文件重寻找xxx对应的值
  • 设置初始化器,使用 getSpringFactoriesInstances(ApplicationContextInitializer.class)找到 ApplicationContextInitializer
  • 设置监听器,使用 getSpringFactoriesInstances(ApplicationListener.class)找到 ApplicationListener
  • 运行 SpringApplication
  • 记录应用启动时间
  • 使用 createBootstrapContext() 创建引导上下文
  • 让当前应用进入headless模式(检测有没有显示器,即使没有显示器,也允许启动)
  • 获取所有的RunListener,底层还是使用 getSpringFactoriesInstances(xxx.class)
  • 遍历所有的RunListener,调用它们的 starting() 方法
  • 保存命令行参数
  • 准备环境,调用 prepareEnvironment()方法
    • 返回或创建环境信息对象
    • 配置环境信息对象
    • 将配置好的环境信息对象绑定到配置源中
    • 监听器调用 environmentPrepared() 方法,通知所有的监听器当前环境准备完成
  • 使用 createApplicationContext() 方法创建IOC容器
    • 根据当前应用类型创建容器,之前判断的应用类型为 SERVLET
  • 使用 prepareContext() 方法,准备IOC容器的基本信息
    • 设置IOC容器的环境信息
    • IOC容器的后置处理流程
    • 应用初始化器,即 applyInitializers()
    • 遍历所有的initializer,调用它们的initialize()方法,对IOC容器进行初始化扩展
    • 监听器调用 contextPrepared() 方法。通知所有的监听器容器准备完成
    • 拿到beanFactory并通过beanFactory注册特定的单实例
    • 监听器调用 contextLoaded() 方法,通知所有的监听器容器加载完成
  • 刷新IOC容器,即 refreshContext(context)
    • 创建IOC容器中的所有组件,关键方法 refresh()
  • 进行容器刷新完成后的一些工作,即 afterRefresh()
  • 监听器调用 started() 方法,通知所有的监听器应用已经启动
  • 调用所有的runners,即 callRunners() ,如果抛出异常,则调用监听器的 failed() 方法
    • 遍历所有的runner,调用它们的 run()方法

感觉自己总结的不太好,后续有时间会继续完善,如有错误请指出

Original: https://www.cnblogs.com/xuzhuo123/p/16085532.html
Author: 在锻炼的新生代农民工
Title: SpringBoot相关原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/586606/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Win10安装WSL

    记录一下安装wsl2的步骤。 安装WSL 1.检查更新win10版本 在 cmd中使用 winver命令来检查当前win的版本,如果版本太低,可以在 设&am…

    Java 2023年5月29日
    0104
  • 字符串数组与字符串指针的区别

    字符指针变量和字符数组的区别: 简单说一下: 字符串数组是用来存放字符串的数组,在内存中占一段连续的单元。所占内存存放的是字符串。定义方法为:char a[N];N为常量表达式,可…

    Java 2023年6月9日
    064
  • 为什么要使用String.intern()

    最近在阅读《深入理解Jav虚拟机》的运行时常量池章节,看到”java语言并不要求常量池一定只有编译器才能产生…运行期间也可以将新的常量放入常量池,这种特性被…

    Java 2023年6月7日
    064
  • Mac 系统如何利用软链接在根目录创建文件夹?

    作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.htm…

    Java 2023年6月13日
    0117
  • 图解用户登录验证流程,写得太好了!

    原文:juejin.cn/post/7025768845075808286 前言 本文通过图示及代码的方式介绍用户登录流程及技术实现,内容包括用户登录,用户验证,如何获取操作用户的…

    Java 2023年5月29日
    072
  • java.util.Properties

    java.util.Properties是对properties这类配置文件的映射。支持key-value类型和xml类型两种。、 key-value类型的配置文件大略长这样: 打…

    Java 2023年5月29日
    069
  • future用法-java中异步执行多个任务并集中处理所有结果

    需求背景 同事问了个问题,java中有什么办法让我异步查询多个表的数据,最后将所有结果进行处理输出想到future是解决这类问题常用的,于是写了个demo给他参考 简化需求 定义3…

    Java 2023年6月9日
    074
  • JAVA入门基础_从零开始的培训_Git入门使用

    Git概述及下载安装 集中式版本控制与分布式版本控制概述 Git下载与安装 Git常用命令 Git所覆盖的区域 全局用户名和邮箱、仓库状态、初始化、添加删除暂存区、提交到本地库、查…

    Java 2023年6月9日
    0109
  • VMware及win10虚拟机的安装及环境配置

    一、安装VMware 1.1、下载VMware安装包 在此给大家一个迅雷的链接:点击下载提取码:sp84 1.2、点击下一步 1.3、点击我接受,点击下一步 1.4、更换安装位置,…

    Java 2023年6月5日
    072
  • rabbitmq简介

    RabbitMQ(Advanced Message Queuing Protocol,高级消息队列协议)是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数…

    Java 2023年5月30日
    070
  • JAVA入门基础_从零开始的培训_JAVA IO流、多线程、集合(四)

    IO流 什么是IO流、能够做什么、分类 IO的基本使用 节点流的基本使用FileInputStream、FileOutputStream、FileReader、FileWriter…

    Java 2023年6月9日
    0140
  • Spring框架的XML扩展特性

    Spring框架从2.0版本开始,提供了基于Schema风格的XML扩展机制,允许开发者扩展spring配置文件。现在我们来看下怎么实现这个功能,可以参考spring帮助文档中的《…

    Java 2023年5月30日
    062
  • java生成随机数、随机英文字母、随机字符串

    以生成4位随机数字举例说明 方式一: (int) (Math.random() * (9999 – 1000 + 1)) + 1000 说明: 随机数范围:1000~9999。 方…

    Java 2023年5月29日
    051
  • Java–Socket通信

    下面内容是Java开发内容的高级知识点,需要对Java中的面向对象、IO、多线程、以及网络相关知识有一定的基础。(知识永远都有深度,本章节长期更新内容) 1、网络基础知识 网络通信…

    Java 2023年5月29日
    070
  • SpringBoot项目接口第一次访问慢的问题

    SpringBoot的接口第一次访问都很慢,通过日志可以发现,dispatcherServlet不是一开始就加载的,有访问才开始加载的,即懒加载。 2019-01-25 15:23…

    Java 2023年5月30日
    072
  • SpringBoot自动装配

    SpringBoot是对Spring的一种扩展,其中比较重要的扩展功能就是自动装配:通过注解对常用的配置做默认配置,简化xml配置内容。本文会对Spring的自动配置的原理和部分源…

    Java 2023年6月8日
    083
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球