Spring MVC流程

SpringMVC启动流程

启动流程

  1. 启动Tomcat

  2. 解析web.xml

  3. 创建DispatcherServlet

  4. 调用DIspatcherServlet的init方法
    4.1 创建Spring容器
    4.2 发布ContextRefresheEvent
    4.3 在OnRefreshed方法中触发initStrategies方法
    4.3.1初始化HandlerMappings (RequestMappingHandler会把@RequestMappping标注的方法解析为HandlerMapping Map<path, 注解信息>, Map<注解信息,方法>)
    4.3.2 初始化HandlerAdapters(不同handler使用不同的Adapter)
    4.3.3 初始化Resolvers...

    默认添加的Handler和Adapter在DispatcherServlet.properties文件中

    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

父子容器

servlet会根据配置文件spring-mvc.xml生成子容器。

复制代码
    <!-- 配置 Spring MVC 的 DispatcherServlet -->
    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 配置 DispatcherServlet 的 URL 映射 -->
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

如何需要父容器,需要如下配置。

Tomcat会先解析listener,根据配置spring.xml生成父容器,子容器可以使用父容器中的Bean。实际应用中其实一个容器就够了。即只使用servlet中的容器。

复制代码
    <!-- 配置 Spring 上下文监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:srping.xml</param-value>
    </context-param>

    <!-- 配置 Spring 的 ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

请求处理

请求是通过DispatcherServlet的doDispatch方法处理。根据path得到handler后处理请求。Adapter处理请求时需要使用Resolver解析参数传递到方法中,方法执行后还需要使用HttpMessageConverter处理返回值转换。

MultipartFile 解析

doDispatch方法在找handler之前会先判断是否是表单文件上传。通过request的Content-Type: mulpart/form-data来判断是MultipartFile,然后MulpartResolver把表单数据解析为multipartFiles,结构为map<filename, standardMultipartFile>。然后会把MultipartFile传到请求的处理方法中。

因此需要配置MultipartFile的Resolver。

复制代码
<multipart-config>
   <location>/tmp</location> <!-- 临时文件存储路径 -->
   <max-file-size>10485760</max-file-size> <!-- 单个文件最大 10MB -->
   <max-request-size>20971520</max-request-size> <!-- 整个请求最大 20MB -->
   <file-size-threshold>1048576</file-size-threshold> <!-- 超过 1MB 的文件写入磁盘 -->
</multipart-config>
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />

参数传递

在 Spring MVC 中,参数处理器(HandlerMethodArgumentResolver)用于将 HTTP 请求中的参数绑定到控制器方法的参数上。

@RequestParam:RequestParamMethodArgumentResolver从request中取param

@PathVariabl:PathVariableMethodArgumentResolver将 URL 路径中的变量绑定到方法参数

@RequestBody:RequestResponseBodyMethodProcessor

@RequestPart:将 multipart 请求中的文件或复杂对象绑定到方法参数。

@RequestHeader:RequestHeaderMethodArgumentResolver将 HTTP 请求头绑定到方法参数

无注解的简单类型:取request与参数名称相同的param

无注解对象类型参数:ModelAttributeMethodProcessor会先调用对象的无参构造器生成一个对象,然后会根据属性名称取param中取参数。如果只有一个属性,即使名称不一致也能赋值。

返回值处理

HttpMessageConverter通过方法返回的类型+Accept接收的格式来确定能否处理。默认提供的HttpMessageConverter只有StringHttpMessageConverter和ByteArrayHttpMessageConverter。因此复杂对象需要引入MappingJackson2HttpMessageConverter。也可以自己定义转换器,通过@InitBinder绑定对象和使用的转换器。

HandlerInterceptor

handlerInterceptor可以在handler处理请求前后添加处理逻辑。

preHandle:请求方法调用之前执行,如果出现错误则停止不执行请求方法。

postHandle:请求方法调用之后执行

afterCompletion:总会执行

相关推荐
谙忆13 小时前
图片CDN与边缘优化实战:缓存策略、图片处理型CDN与动态裁剪
java·spring·缓存
web行路人15 小时前
Spring Boot 第四周:集成 Spring Security 与 JWT 鉴权实践
spring boot·后端·spring
Xiaouuuuua16 小时前
Superpowers:GPT-5.6 时代下的流程毒瘤
java·gpt·spring
一生有你202017 小时前
Spring AI ChatMemory 监控与排查:从指标到排错全链路
java·人工智能·spring
折哥的程序人生 · 物流技术专研17 小时前
Java 23 种设计模式:从踩坑到精通 | 番外:状态 vs 策略 —— if-else 消除的两条路,你走对了吗?
java·spring·状态模式·策略模式·java面试·java设计模式·从踩坑到精通
折哥的程序人生 · 物流技术专研18 小时前
Java 23 种设计模式:从踩坑到精通 | 番外:工厂方法 vs 抽象工厂 —— 从单产品到产品族,架构如何演进?
java·spring·java面试·抽象工厂·工厂方法·java设计模式·从踩坑到精通
砍材农夫18 小时前
spring|spring event|Spring内置事件驱动编程模型
java·数据库·spring
heimeiyingwang1 天前
【架构实战】缓存一致性:Cache Aside与双写问题的破解
spring·缓存·架构
栈溢出了1 天前
Redis 分片集群与哈希槽笔记
java·redis·笔记·spring
一路向北North2 天前
Spring Security OAuth2.0(20):完善环境配置
java·后端·spring