Loading

SpringBoot相关原理

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()方法

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

posted @ 2022-04-01 10:21  KeepGoing4everZxz  阅读(122)  评论(0编辑  收藏  举报