String MVC 原理详解
SpringMVC 是基于 Servlet 为核心,核心组件为DispatcherServlet。其基于Tomcat或Jetty或Undertow或WebSphere。
模型(Model):模型表示应用程序的数据、业务逻辑和规则。它是系统的核心,负责管理数据、状态以及与数据库或外部服务的交互。
视图(View):视图负责向用户呈现数据,是用户与应用程序交互的界面。
控制器(Controller):控制器是模型和视图之间的中介,负责处理用户输入并协调模型与视图的交互。
工作原理:
- 用户通过视图(如点击按钮、提交表单)与应用程序交互。
- 控制器接收用户输入,解析请求,并调用相应的模型进行数据处理。
- 模型执行必要的业务逻辑,更新数据状态,并通知视图需要更新。
- 视图根据模型的最新数据重新渲染,呈现给用户。
- 循环重复此过程,保持用户界面的动态更新。
SpringBoot 加载 DispatcherServlet
若按照XML的形式配置文件,需要在Tomcat中的web.xml中添加DispatcherServlet
1 |
|
此XML配置下,所有的请求都会被传递给DispatcherServlet
处理。默认 DispatcherServlet 会加载 WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml
配 置 文 件 。 本 示 例 为 /WEB-INF/spring-mvc-config.xml
其中会注册一些bean类。
若按照Java注解配置,SpringBoot会根据自动配置将DispatcherServlet
传递给内置Tomcat
在springboot启动过程中,会处理自动配置注解@EnableAutoConfiguration
,其中通过@Import(AutoConfigurationImportSelector.class)
注解执行AutoConfigurationImportSelector.class
的selectImports()
方法。在此方法中会将路径中所有的org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中的配置类读入。其中与MVC的有关的主要类是org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
和org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
和org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
其中
WebMvcAutoConfiguration
中存储Spring Web MVC的一些默认配置;DispatcherServletAutoConfiguration
中注册两个bean类DispatcherServlet
和DispatcherServletRegistrationBean
后者中会注入前者,并在Servlet容器(如Tomcat)启动时会将前者的Sevlet类自动加载。即在内置Servlet容器时会自动执行DispatcherServletRegistrationBean
的父类方法ServletContextInitializer
的onStartup()
方法,将DispatcherServlet
注册到Servlet容器中;SpringBoot启动过程中,因为是 web servlet 所以执行
ServletWebServerApplicationContext.onRefresh().createWebServer()
其中会调用TomcatServletWebServerFactory.getWebServer()
其中会创建 Tomcat类并在prepareContext(tomcat.getHost(), initializers);
中将SpringBoot中的ServletContextInitializer
注册进Tomcat中。ServletWebServerFactoryAutoConfiguration
会将SpringBoot支持的Servlet容器根据条件自动导入,包括EmbeddedTomcat
EmbeddedJetty
EmbeddedUndertow
然后注册两个bean类ServletWebServerFactoryCustomizer
和ServletWebServerFactoryCustomizer
DispatcherServlet 处理请求
DispatcherServlet主要组成:
- HandlerMapping:映射请求
- HandlerAdapter:执行控制器
- ViewResolver:用于解析视图(如Thymeleaf 或 JSP)。
- HttpMessageConverter:支持JSON/XML的序列化和反序列化
当DispatcherServlet注册到Tomcat中后,每当请求进入后会自动执行DispatcherServlet的Service()
方法,在其父类FrameworkServlet
中
1 |
|
最终都会执行processRequest(request, response)
方法,其中会执行doService(request, response)
方法,DispatcherServlet.doService()
会执行DispatcherServlet.doDispatch(request, response)
主要步骤有:
1 | protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
mappedHandler = getHandler(processedRequest);
通过handlerMappings
寻找符合条件的handler(即Controller)getHandlerAdapter(mappedHandler.getHandler());
找到适配handler
的适配器HandlerAdapter
ha.handle(processedRequest, response, mappedHandler.getHandler());
将handler
传入适配的HandlerAdapter
并执行其hanlder()
方法,适配器调用处理器方法(如@RequestMapping
方法),生成ModelAndView
processDispatchResult()
:通过ViewResolver
解析视图 和 统一处理异常,生成错误响应。
整体处理流程:
- Servlet 容器(Tomcat)的请求转发:
Tomcat 接收请求:客户端请求首先由 Tomcat 的
Connector
接收,解析 HTTP 协议后生成HttpServletRequest
和HttpServletResponse
对象。Servlet 匹配:Tomcat 根据 URL 映射规则,将请求转发到对应的
Servlet
(此处为DispatcherServlet
)。
DispatcherServlet
的初始化流程:
service()
方法:Tomcat 调用DispatcherServlet
的service()
方法(继承自HttpServlet
)。doService()
方法:DispatcherServlet
重写了doService()
,在其中初始化一些上下文(如LocaleContext
、RequestAttributes
),然后调用doDispatch()
。
doDispatch()
的核心逻辑:doDispatch()
负责以下关键步骤:- 处理 Multipart 请求(如文件上传)。
- 获取处理器链(
HandlerExecutionChain
),包含目标处理器和拦截器。 - **调用拦截器的
preHandle()
**。 - 执行处理器方法(如
@Controller
中的方法),生成ModelAndView
。 - **调用拦截器的
postHandle()
**。 - 处理结果(渲染视图或处理异常)。
视图渲染与响应写入
- **
processDispatchResult()
**:在doDispatch()
的末尾,调用此方法处理视图渲染或异常。- 视图渲染:通过
ViewResolver
解析视图,调用View.render()
将模型数据写入响应。 - 异常处理:通过
HandlerExceptionResolver
生成错误响应。
- 视图渲染:通过
请求类型
GET
1 |
1 |
POST
1 |
1 |
其他请求类型限制:
1 | // 必须有id参数 |
请求数据格式
Form格式
前端代码
1 | <form action="/login" method="post"> |
传输数据
1 | POST /login HTTP/1.1 |
后端代码
1 |
|
JSON格式
JavaScript代码
1 | fetch('/login', { |
传输数据
1 | POST /login HTTP/1.1 |
后端代码
1 |
|
请求路径格式
普通 URL 路径映射
@RequestMapping(value={"/test1", "/user/create"})
URI 模板模式映射
@RequestMapping(value="/users/{userId}")
@RequestMapping(value="/users/{userId}/create")
@RequestMapping(value="/users/{userId}/topics/{topicId}")
需要通过@PathVariable
来获取URL中参数。
1 |
|
Ant 风格的 URL 路径映射
@RequestMapping(value="/users/**")
:可以匹配/users/abc/abc
,但/users/123
将会被【URI模板模式映射 中的/users/{Id}
模式优先映射到】@RequestMapping(value="/product?")
:可匹配/product1
或/producta
,但不匹配/product
或/productaa
@RequestMapping(value="/product*")
:可匹配/productabc
或/product
,但不匹配/productabc/abc
@RequestMapping(value="/product/*")
:可匹配/product/abc
,但不匹配/productabc
@RequestMapping(value="/products/**/{productId}")
:可匹配/products/abc/abc/123
或/products/123
,也就是Ant风格和URI模板变量风格可混用
正则表达式风格的 URL 路径映射
@RequestMapping(value="/products/{categoryCode:\\d+}-{pageNumber:\\d+}")
:可 以 匹 配 /products/123-1
,但不能匹配/products/abc-1
,这样可以设计更加严格的规则。
@RequestMapping("/{textualPart:[a-z-]+}-{numericPart:[\\d]+}")
SpringMVC注解
控制器相关注解
@Controller
- 标记一个类为 Spring MVC 控制器,负责处理 HTTP 请求。
- 示例:@Controller public class MyController { … }
@RestController
- 组合注解,等价于 @Controller + @ResponseBody,表示控制器方法返回的对象直接序列化为 JSON 或 XML。
- 示例:@RestController public class ApiController { … }
@Component
- 通用注解,可用于控制器类,纳入 Spring 容器管理(通常搭配其他注解使用)。
@RequestMapping
- 映射 HTTP 请求到控制器方法或类,支持 GET、POST 等方法。
- 属性:value(路径)、method(请求方法)、produces(响应类型)、consumes(请求类型)。
- 示例:@RequestMapping(value = “/home”, method = RequestMethod.GET)
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
、@PatchMapping
- @RequestMapping 的快捷方式,分别对应 HTTP 的 GET、POST、PUT、DELETE 和 PATCH 请求。
- 示例:@GetMapping(“/users”)
请求参数与路径处理注解
@PathVariable
- 从 URL 路径中提取变量,绑定到方法参数。
- 示例:@GetMapping(“/user/{id}”) public String getUser(@PathVariable Long id)
@RequestParam
- 从请求参数(查询字符串或表单数据)中提取值,绑定到方法参数。
- 属性:name(参数名)、required(是否必须)、defaultValue(默认值)。
- 示例:@RequestParam(value = “name”, defaultValue = “Guest”) String name
@RequestBody
- 将 HTTP 请求的正文(如 JSON 或 XML)绑定到方法参数,通常用于 RESTful API。
- 示例:@PostMapping(“/user”) public void saveUser(@RequestBody User user)
@RequestHeader
- 从 HTTP 请求头中提取值,绑定到方法参数。
- 示例:@RequestHeader(“User-Agent”) String userAgent
@CookieValue
- 从请求的 Cookie 中提取值,绑定到方法参数。
- 示例:@CookieValue(“sessionId”) String sessionId
@MatrixVariable
- 从 URL 路径中的矩阵变量(如 /path;key=value)提取值。
- 示例:@GetMapping(“/data/{path}”) public String getMatrix(@MatrixVariable String key)
响应处理注解
@ResponseBody
- 表示方法返回值直接作为 HTTP 响应正文,通常序列化为 JSON 或 XML。
- 示例:@ResponseBody public User getUser()
@ResponseStatus
- 指定控制器方法或异常处理方法的 HTTP 状态码。
- 示例:@ResponseStatus(HttpStatus.CREATED)
@ModelAttribute
- 将方法返回值或参数绑定到模型对象,供视图使用;也可用于方法级,预填充模型数据。
- 示例:@ModelAttribute(“user”) public User getUser()
异常处理注解
@ExceptionHandler
- 标记方法用于处理特定异常,限定在控制器内部。
- 示例:@ExceptionHandler(NullPointerException.class) public ResponseEntity handleException()
@ControllerAdvice
- 定义全局异常处理、模型增强或绑定器,作用于所有控制器。
- 示例:@ControllerAdvice public class GlobalExceptionHandler { … }
跨域与配置相关注解
@CrossOrigin
- 启用跨域资源共享(CORS),可用于类或方法级别。
- 属性:origins(允许的域名)、methods(允许的请求方法)。
- 示例:@CrossOrigin(origins = “http://example.com“)
@SessionAttributes
- 指定模型属性存储在会话(Session)中,作用于类级别。
- 示例:@SessionAttributes(“user”)
@InitBinder
- 标记方法用于自定义数据绑定或验证逻辑,作用于控制器内部。
- 示例:@InitBinder public void initBinder(WebDataBinder binder)
其他高级注解
@SessionAttribute
- 从会话中获取属性值,绑定到方法参数。
- 示例:@SessionAttribute(“user”) User user
@RequestAttribute
- 从请求属性中获取值,绑定到方法参数。
- 示例:@RequestAttribute(“data”) String data
@EnableWebMvc
- 启用 Spring MVC 配置,通常用于自定义 MVC 配置类。
- 示例:@EnableWebMvc @Configuration public class WebConfig { … }
建议
对于前后端分离的项目可以选择RESTful风格的API,使用JSON作为传递类型。通过@RequestBody
(将HTTP的body使用JSON反序列化为Java对象) 和 @ResponseBody
(将返回数据使用JSON序列化并赋值到HTTP的body中)加载请求和响应。
对于文件等二进制传输,使用Multipart/form-data
格式传输:
前端上传代码:
1 | const formData = new FormData(); |