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)

大家都在看

  • springboot项目打包、部署Tomcat流程详解

    项目打包1.移除springboot内置tomcat,在pom文件中找到spring-boot-starter-web依赖,替换成以下依赖。(仅在项目打包时移除内置tomcat) …

    Java 2023年5月30日
    087
  • mybatis 拦截器

    1.mybatis拦截器介绍 拦截器可在mybatis进行sql底层处理的时候执行额外的逻辑,最常见的就是分页逻辑、对结果集进行处理过滤敏感信息等。 public Paramete…

    Java 2023年6月9日
    088
  • 无重复字符的最长子串

    https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ 上链接为Leeco…

    Java 2023年6月5日
    071
  • 医惠集成平台调研方案分析(二)

    业务标签:医院信息集成平台、互联网医院、互联网护理、慢性病随访 技术标签:ESB、ETL+CDC、NLP、FaaS、SaaS、Hadoop、MicroService 技术微信群:加…

    Java 2023年5月29日
    069
  • 三、《微服务:从设计到部署》–进程间通信IPC

    交互方式: 在单体应用中,组件可通过语言级方法或者函数相互调用。相比之下,基于微服务的应用是一个运行在多台机器上的分布式系统。通常,每个服务实例都是一个进程。因此,服务必须使用进程…

    Java 2023年6月5日
    0104
  • 建造者模式(创建型)

    建造者模式 介绍 建造者模式注重的是部件构建的过程,意在 通过一步一步地精确构造出一个复杂的对象。 可以将建造者模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Com…

    Java 2023年6月15日
    083
  • 【力扣】83. 删除排序链表中的重复元素

    存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。返回同样按升序排列的结果链表。 示例 1: 输入:head = [1,…

    Java 2023年6月8日
    069
  • 数据迁移的套路

    数据迁移的类型 随着业务的发展,存储也会经常性的需要迁移。以下场景是我们开发过程中经常遇到的 业务、团队在快速扩张,需要适当时机进行微服务的拆分,需要独立的数据库,将数据从源数据库…

    Java 2023年6月8日
    089
  • 为博客添加评论邮件提醒功能(踩坑全过程)

    在很早之前,我就想过要给博客加一个邮件评论提醒功能,上个月折腾了三天愣是没折腾出什么,这几天学不进去,刚好来折腾这个。 因为懒,需要加载到图床的图片太多,这次不插入图片,改用引用其…

    Java 2023年6月7日
    069
  • MySQL查询之内连接,外连接查询场景的区别与不同

    前言 我在写 sql查询的时候,用的最多的就是 where条件查询,这种查询也叫内连查询 inner join,当然还有外连查询 outer join, 左&…

    Java 2023年6月13日
    082
  • 数据结构笔记—第一篇 数据结构概述

    第一篇数据结构概述 1.数据结构概述 什么是数据结构? 简单来说,就是计算机存储,组织数据的方式;它包含三方面的内容,逻辑关系、存储关系及操作。 记住关键字:存储—&#…

    Java 2023年6月16日
    081
  • redis知识点

    主从 单线程高并发 epoll 丰富类型(string,hashtable,list,set,sortdeset) 计算向数据移动:与memcached相比,支持类型,意味着可在服…

    Java 2023年6月9日
    0142
  • Java之NIO

    下面展示自己代码熟悉Java的NIO编程的笔记。 1、缓冲区(Buffer) /* 一、缓冲区(Buffer):在Java 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的…

    Java 2023年5月29日
    080
  • 操作线程的方法

    操作线程的方法操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态。 线程的休眠能控制线程行为的方法之一是调用sleep()方法,sleep()方法可以指定线程休眠的…

    Java 2023年6月9日
    082
  • 如何正确的中断线程?你的姿势是否正确

    Java停止线程的逻辑(协同、通知) 在Java程序中,我们想要停止一个线程可以通过interrupt方法进行停止。但是当我们调用interrupt方法之后,它可能并不会立刻就会停…

    Java 2023年6月5日
    080
  • JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)

    一、前言 在多线程的场景下,我们会经常使用加锁,来保证线程安全。如果锁用的不好,就会陷入死锁,我们以前可以使用 Object的 wait/notify来解决死锁问题。也可以使用 C…

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