一、Spring MVC核心架构回顾
Spring MVC是Spring框架的Web模块,基于Servlet API构建,采用前端控制器模式。其核心架构围绕DispatcherServlet展开,通过一系列策略接口实现请求处理的高度可配置性。
核心流程图 :

二、方法参数解析机制详解
2.1 参数解析器体系
Spring MVC通过HandlerMethodArgumentResolver接口实现方法参数的解析,共有近30种实现类处理不同类型的参数:
// 核心解析器接口
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception;
}
2.2 常见参数解析器
| 解析器 | 处理注解 | 数据来源 |
|---|---|---|
RequestParamMethodArgumentResolver |
@RequestParam或简单类型 |
请求参数 |
PathVariableMethodArgumentResolver |
@PathVariable |
URL路径变量 |
RequestHeaderMethodArgumentResolver |
@RequestHeader |
请求头 |
SessionAttributeMethodArgumentResolver |
@SessionAttribute |
Session属性 |
RequestBodyAdviceArgumentResolver |
@RequestBody |
请求体 |
ServletModelAttributeMethodProcessor |
无(非简单类型) | 请求参数+对象绑定 |
2.3 参数解析流程
// 简化版解析流程
public Object resolveArgument(MethodParameter parameter, ...) {
// 1. 获取参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
// 2. 解析参数值
Object arg = resolver.resolveArgument(parameter, ...);
// 3. 类型转换(如果需要)
if (arg != null && !parameter.getParameterType().isInstance(arg)) {
arg = typeConverter.convertIfNecessary(arg, parameter.getParameterType());
}
return arg;
}
2.4 类型转换机制
当参数类型不是String时,Spring MVC需要进行类型转换:
情况一:自定义PropertyEditor
// 自定义编辑器
public class StringToUserEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) {
User user = new User();
user.setName(text);
setValue(user);
}
}
// 注册编辑器
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(User.class, new StringToUserEditor());
}
情况二:构造方法转换
如果类型有String参数的构造方法,Spring会尝试使用:
// User类
public class User {
private String name;
public User(String name) { // Spring会尝试使用此构造方法
this.name = name;
}
}
// Controller方法
@GetMapping("/test")
public String test(@RequestParam User user) {
return user.getName(); // "zhouyu" → new User("zhouyu")
}
源码位置 :TypeConverterDelegate.convertIfNecessary()
三、文件上传(MultipartFile)源码解析
3.1 Servlet原生文件上传
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
// 获取所有Part
Collection<Part> parts = request.getParts();
for (Part part : parts) {
String header = part.getHeader("content-disposition");
// form-data; name="file"; filename="test.jpg"
if (part.getSubmittedFileName() != null) {
// 文件Part
part.write("/upload/" + part.getSubmittedFileName());
} else {
// 文本Part
String value = request.getParameter(part.getName());
}
}
}
}
3.2 Spring MVC文件上传封装
3.2.1 MultipartResolver
Spring MVC通过MultipartResolver判断是否为文件上传请求:
// DispatcherServlet中
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// 检查是否为multipart请求
processedRequest = checkMultipart(request);
// ...
}
private HttpServletRequest checkMultipart(HttpServletRequest request) {
if (this.multipartResolver != null &&
this.multipartResolver.isMultipart(request)) {
// 转换为MultipartHttpServletRequest
return this.multipartResolver.resolveMultipart(request);
}
return request;
}
3.2.2 文件参数解析
当方法参数为MultipartFile类型时:
@PostMapping("/upload")
public String upload(MultipartFile file,
@RequestPart String description) {
// file: 文件Part,封装为MultipartFile
// description: 表单文本字段
file.transferTo(new File("/upload/" + file.getOriginalFilename()));
return "success";
}
注意 :@RequestPart vs @RequestParam
-
@RequestParam:获取请求参数(包括URL参数和表单字段) -
@RequestPart:只获取multipart请求的表单字段
3.3 文件上传源码实现
// StandardMultipartHttpServletRequest的封装过程
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
protected void initializeMultipart() {
// 获取所有Part
Collection<Part> parts = getParts();
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
for (Part part : parts) {
String filename = part.getSubmittedFileName();
if (filename != null) {
// 文件Part → StandardMultipartFile
files.add(part.getName(), new StandardMultipartFile(part));
} else {
// 文本Part
params.add(part.getName(), getParameter(part.getName()));
}
}
setMultipartFiles(files);
setMultipartParameters(params);
}
}
四、方法返回值处理机制
4.1 返回值处理器体系
Spring MVC通过HandlerMethodReturnValueHandler处理不同类型的返回值:
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception;
}
4.2 常用返回值处理器
| 处理器 | 处理类型 | 说明 |
|---|---|---|
ModelAndViewMethodReturnValueHandler |
ModelAndView |
直接使用ModelAndView |
RequestResponseBodyMethodProcessor |
@ResponseBody |
将返回值写入响应体 |
ViewNameMethodReturnValueHandler |
String(无@ResponseBody) |
作为视图名解析 |
ModelMethodProcessor |
Model |
添加到Model中 |
4.3 @ResponseBody处理流程
4.3.1 核心处理器:RequestResponseBodyMethodProcessor
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
// 1. 标记请求已处理
mavContainer.setRequestHandled(true);
// 2. 获取输入输出消息
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// 3. 使用HttpMessageConverter写入响应
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
4.3.2 HttpMessageConverter选择策略
Spring MVC根据返回值类型 和请求的Accept头选择合适的Converter:
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage,
ServletServerHttpResponse outputMessage) {
// 获取可接受的MediaType
List<MediaType> acceptableTypes = getAcceptableMediaTypes(inputMessage);
// 获取支持的MediaType
List<MediaType> producibleTypes = getProducibleMediaTypes(outputMessage, value, returnType);
// 选择最合适的MediaType
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
// 选择Converter并写入
for (MediaType mediaType : mediaTypesToUse) {
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(value.getClass(), mediaType)) {
// 写入响应
((HttpMessageConverter<T>) converter).write(value, mediaType, outputMessage);
return;
}
}
}
}
4.4 自定义HttpMessageConverter
当默认Converter不满足需求时,可以自定义:
// 将User对象转为纯文本的自定义Converter
public class UserToTextHttpMessageConverter extends AbstractHttpMessageConverter<User> {
public UserToTextHttpMessageConverter() {
// 支持所有MediaType
super(MediaType.ALL);
}
@Override
protected boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
@Override
protected User readInternal(Class<? extends User> clazz,
HttpInputMessage inputMessage) {
// 读取逻辑(不需要实现,因为是输出)
return null;
}
@Override
protected void writeInternal(User user, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// 将User的name字段作为纯文本输出
String text = "User: " + user.getName();
outputMessage.getBody().write(text.getBytes(StandardCharsets.UTF_8));
}
}
// 配置自定义Converter
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new UserToTextHttpMessageConverter());
}
}
五、Spring MVC父子容器机制
5.1 传统web.xml配置方式
<web-app>
<!-- 1. 父容器(ContextLoaderListener创建) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 2. 子容器(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>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5.2 父子容器创建流程
5.2.1 父容器创建(ContextLoaderListener)
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// 创建父容器
WebApplicationContext rootContext = initWebApplicationContext(event.getServletContext());
// 存储到ServletContext中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
rootContext);
}
}
5.2.2 子容器创建(DispatcherServlet)
public abstract class FrameworkServlet extends HttpServletBean {
protected WebApplicationContext initWebApplicationContext() {
// 1. 获取父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 2. 创建子容器
WebApplicationContext wac = createWebApplicationContext(rootContext);
// 3. 刷新子容器(加载配置、创建Bean)
configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
5.3 父子容器分工
| 容器 | 配置位置 | 包含的Bean | 作用 |
|---|---|---|---|
| 父容器 | /WEB-INF/applicationContext.xml |
Service、Repository、DataSource等 | 业务逻辑层、数据访问层 |
| 子容器 | /WEB-INF/spring-mvc.xml |
Controller、HandlerMapping、ViewResolver等 | Web层组件 |
重要规则:子容器可以访问父容器的Bean,但父容器不能访问子容器的Bean。
5.4 父子容器隔离的好处
-
职责分离:Web层和业务层明确分离
-
配置独立:可以独立配置扫描路径、AOP等
-
灵活性:可以配置多个DispatcherServlet共享父容器
-
避免冲突:防止Controller被意外注入Service层
六、@EnableWebMvc深度解析
6.1 @EnableWebMvc的作用
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class) // 关键!
public @interface EnableWebMvc {
}
6.2 DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
// 收集所有WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
// 代理所有配置方法
@Override
protected void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
// 其他配置方法类似...
}
6.3 配置Spring MVC的三种方式
方式一:实现WebMvcConfigurer(推荐)
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
}
}
方式二:继承WebMvcConfigurationSupport
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
// 重写特定方法进行配置
}
方式三:使用@Bean注解
@Configuration
public class WebConfig {
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
// 自定义HandlerAdapter
return new RequestMappingHandlerAdapter();
}
}
6.4 @EnableWebMvc的HandlerMapping顺序
// 使用@EnableWebMvc时
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// RequestMappingHandlerMapping优先级最高
return new RequestMappingHandlerMapping();
}
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
// BeanNameUrlHandlerMapping优先级第二
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(1); // RequestMappingHandlerMapping默认order为0
return mapping;
}
重要影响:当同一路径被不同HandlerMapping匹配时,order值小的优先处理。
七、WebApplicationInitializer:无web.xml配置
7.1 替代web.xml的现代方式
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// 1. 创建Spring容器
AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// 2. 创建DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(context);
// 3. 注册Servlet
ServletRegistration.Dynamic registration =
servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/*");
}
}
7.2 工作原理:ServletContainerInitializer
Spring MVC通过SPI机制注册SpringServletContainerInitializer:
-
META-INF/services/javax.servlet.ServletContainerInitializer文件内容:
org.springframework.web.SpringServletContainerInitializer -
SpringServletContainerInitializer实现:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) { // 1. 实例化所有WebApplicationInitializer List<WebApplicationInitializer> initializers = new ArrayList<>(); // 2. 排序并执行 AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
八、拦截器(Interceptor)源码解析
8.1 拦截器接口
public interface HandlerInterceptor {
// 前置处理
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
return true;
}
// 后置处理(渲染视图前)
default void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
}
// 完成处理(渲染视图后)
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
}
}
8.2 拦截器执行流程
// HandlerExecutionChain中的执行逻辑
public class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request,
HttpServletResponse response) throws Exception {
// 顺序执行preHandle
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果返回false,触发afterCompletion(已执行的拦截器)
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i; // 记录最后一个成功的拦截器
}
return true;
}
void applyPostHandle(HttpServletRequest request,
HttpServletResponse response,
ModelAndView mv) throws Exception {
// 逆序执行postHandle
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
8.3 拦截器执行时序图
8.4 拦截器异常流程时序图(preHandle返回false)

8.5详细执行流程时序图

九、RequestMappingHandlerMapping初始化
9.1 映射注册流程
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
protected void initHandlerMethods() {
// 1. 扫描所有Bean
String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
Class<?> beanType = getType(beanName);
// 2. 判断是否为Handler(有@Controller或@RequestMapping)
if (isHandler(beanType)) {
// 3. 解析Handler方法
detectHandlerMethods(beanName);
}
}
}
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = getType(handler);
// 4. 遍历所有方法
Map<Method, RequestMappingInfo> methods = MethodIntrospector.selectMethods(
handlerType,
(Method method) -> getMappingForMethod(method, handlerType)
);
// 5. 注册映射
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, handlerType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
9.2 映射存储结构
public class MappingRegistry {
// 路径→映射信息
private final Map<String, List<RequestMappingInfo>> pathLookup = new HashMap<>();
// 映射信息→HandlerMethod
private final Map<RequestMappingInfo, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// HandlerMethod→映射信息
private final Map<HandlerMethod, RequestMappingInfo> registry = new HashMap<>();
// 跨域配置
private final Map<RequestMappingInfo, CorsConfiguration> corsLookup = new HashMap<>();
}
十、总结与最佳实践
10.1 核心要点回顾
-
参数解析 :通过
HandlerMethodArgumentResolver实现,支持类型转换 -
文件上传 :基于Servlet的Part API封装为
MultipartFile -
返回值处理 :通过
HandlerMethodReturnValueHandler和HttpMessageConverter实现 -
父子容器:实现Web层与业务层分离,子容器可访问父容器Bean
-
配置方式 :支持web.xml、
@EnableWebMvc、WebApplicationInitializer -
拦截器:提供请求处理的前置、后置、完成三个阶段拦截
10.2 性能优化建议
-
合理使用参数注解:明确指定参数来源,减少解析开销
-
缓存HandlerMethod:避免重复解析方法元数据
-
选择合适的MessageConverter:只添加必要的Converter
-
合理配置拦截器:避免在拦截器中执行耗时操作
10.3 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 参数绑定失败 | 类型转换失败 | 添加PropertyEditor或Converter |
| 文件上传失败 | 未配置MultipartResolver | 配置CommonsMultipartResolver |
| 中文乱码 | 字符编码不一致 | 统一使用UTF-8编码 |
| 拦截器不生效 | 顺序问题或路径匹配 | 检查order和pathPatterns |
10.4 扩展开发指南
-
自定义参数解析器 :实现
HandlerMethodArgumentResolver -
自定义返回值处理器 :实现
HandlerMethodReturnValueHandler -
自定义HttpMessageConverter :继承
AbstractHttpMessageConverter -
自定义HandlerMapping :实现
HandlerMapping接口