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()
- 创建IOC容器中的所有组件,关键方法
- 进行容器刷新完成后的一些工作,即
afterRefresh()
- 监听器调用
started()
方法,通知所有的监听器应用已经启动 - 调用所有的runners,即
callRunners()
,如果抛出异常,则调用监听器的failed()
方法- 遍历所有的runner,调用它们的
run()
方法
- 遍历所有的runner,调用它们的
感觉自己总结的不太好,后续有时间会继续完善,如有错误请指出