一. spring mvc关键组件
spring mvc 的核心请求处理链路,可以概括为:dispatcherservlet 接收请求 → 找到对应的 handler 执行 → 把执行结果解析成视图并返回。整个过程看起来简单,背后其实是由几个核心组件分工协作完成的。理解这些组件的职责,是后面阅读源码、排查问题的关键。下面先看其中三个最重要的组件:
| 组件 | 描述 | 常用组件 |
|---|---|---|
处理器映射器:handlermapping | 匹配映射路径对应的 handler,返回可执行的处理器链对象 handlerexecutionchain 对象 | requestmappinghandlermapping |
处理器适配器:handleradapter | 匹配 handlerexecutionchain 对应的适配器进行处理器调用,返回视图模型对象 | requestmappinghandleradapter |
视图解析器:viewresolver | 对视图模型对象进行解析 | internalresourceviewresolver |
springmvc的默认组件,springmvc 在前端控制器 dispatcherservlet加载时,就会进行初始化操作,在进行初始化时,就会加载springmvc默认指定的一些组件,这些默认组件配置在 dispatcherservlet.properties 文件中,该文件存在与spring-webmvc-5.3.7.jar包下的 org\springframework\web\servlet\ 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.function.support.routerfunctionmapping 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,\ org.springframework.web.servlet.function.support.handlerfunctionadapter org.springframework.web.servlet.viewresolver=org.springframework.web.servlet.view.internalresourceviewresolver
二. springmvc的组件原理剖析
2.1 前端控制器初始化
前端控制器dispatcherservlet是springmvc的入口,也是springmvc的大脑,主流程的工作都是在此完成的,梳理一下dispatcherservlet 代码。dispatcherservlet 本质是个servlet,当配置了 load-on-startup 时,会在服务器启动时就执行创建和执行初始化init方法,每次请求都会执行service方法。
dispatcherservlet 的继承体系:

1. 初始化入口:init()
httpservletbean 重写了 init() 方法,并且把它声明为 final。它主要做两件事:
- 读取
web.xml中的init-param参数,例如 contextconfiglocation; - 调用
initservletbean(),把具体初始化工作交给子类。
public final void init() throws servletexception {
// 读取并设置 init-param 参数
propertyvalues pvs = new servletconfigpropertyvalues(getservletconfig(), this.requiredproperties);
// ...
// 交给子类做具体初始化
initservletbean();
}2. frameworkservlet 初始化 spring 容器
frameworkservlet 实现了 initservletbean(),核心是初始化 webapplicationcontext:
protected final void initservletbean() throws servletexception {
// 初始化 web 容器
this.webapplicationcontext = initwebapplicationcontext();
initframeworkservlet();
}
initwebapplicationcontext() 主要完成三件事:
protected webapplicationcontext initwebapplicationcontext() {
// 1. 获取根容器,作为当前容器的父容器
webapplicationcontext rootcontext = webapplicationcontextutils.getwebapplicationcontext(getservletcontext());
// 2. 创建 servlet 自己的 webapplicationcontext
webapplicationcontext wac = createwebapplicationcontext(rootcontext);
// 3. 刷新容器,触发 bean 的创建
wac.refresh();
return wac;
}父容器和子容器概念和关系:
- 父容器:spring 通过contextloaderlistener为入口产生的applicationcontext容器,内部主要维护的是
applicationcontext.xml(或相应配置类)配置的bean信息; - 子容器:springmvc通过dispatcherservlet的init() 方法产生的applicationcontext容器,内部主要维护的是
spring-mvc.xml(或相应配置类)配置的bean信息,且内部还通过parent属性维护这父容器的引用。 - bean的检索顺序:根据上面子父容器的概念,可以知道controller存在与子容器中,而controller中要注入service时,会先从子容器本身去匹配,匹配不成功时在去父容器中去匹配,于是最终从父容器中匹配到的userservice,这样子父容器就可以进行联通了。但是父容器只能从自己容器中进行匹配,不能从子容器中进行匹配。
3. dispatcherservlet 初始化九大组件
spring 容器刷新完成后,会回调 onrefresh() 方法,dispatcherservlet 重写了它:
@override
protected void onrefresh(applicationcontext context) {
initstrategies(context);
}
initstrategies() 是初始化 spring mvc 组件的核心方法,它会初始化九大组件:
protected void initstrategies(applicationcontext context) {
initmultipartresolver(context); // 文件上传解析器
initlocaleresolver(context); // 区域信息解析器
initthemeresolver(context); // 主题解析器
inithandlermappings(context); // 处理器映射器
inithandleradapters(context); // 处理器适配器
inithandlerexceptionresolvers(context); // 异常解析器
initrequesttoviewnametranslator(context); // 请求转视图名
initviewresolvers(context); // 视图解析器
initflashmapmanager(context); // flashmap 管理器
}
2.2 前端控制器执行主流程

1. 入口方法
初始化完成后,每次请求进来,执行链路为:
httpservlet.service()
→ frameworkservlet.service()
→ processrequest()
→ dispatcherservlet.doservice()
→ dispatcherservlet.dodispatch()
其中 dodispatch() 是处理请求的主流程方法,spring mvc 的所有核心逻辑几乎都从这里经过。
2. dodispatch 源码全览
protected void dodispatch(httpservletrequest request, httpservletresponse response) throws exception {
httpservletrequest processedrequest = request;
handlerexecutionchain mappedhandler = null;
boolean multipartrequestparsed = false;
webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request);
try {
modelandview mv = null;
exception dispatchexception = null;
try {
// 1. 检查是否是文件上传请求,如果是则解析 multipart
processedrequest = checkmultipart(request);
multipartrequestparsed = (processedrequest != request);
// 2. 根据请求找到 handler(以及对应的拦截器链)
mappedhandler = gethandler(processedrequest);
if (mappedhandler == null) {
nohandlerfound(processedrequest, response);
return;
}
// 3. 找到能执行该 handler 的适配器
handleradapter ha = gethandleradapter(mappedhandler.gethandler());
// 4. 执行拦截器的 prehandle
if (!mappedhandler.applyprehandle(processedrequest, response)) {
return;
}
// 5. 真正调用 handler,返回 modelandview
mv = ha.handle(processedrequest, response, mappedhandler.gethandler());
if (asyncmanager.isconcurrenthandlingstarted()) {
return;
}
// 6. 如果没有指定视图名,生成默认视图名
applydefaultviewname(processedrequest, mv);
// 7. 执行拦截器的 posthandle
mappedhandler.applyposthandle(processedrequest, response, mv);
}
catch (exception ex) {
dispatchexception = ex;
}
catch (throwable err) {
dispatchexception = new nestedservletexception("handler dispatch failed", err);
}
// 8. 处理结果:异常解析、视图渲染、aftercompletion
processdispatchresult(processedrequest, response, mappedhandler, mv, dispatchexception);
}
catch (exception ex) {
triggeraftercompletion(processedrequest, response, mappedhandler, ex);
}
catch (throwable err) {
triggeraftercompletion(processedrequest, response, mappedhandler,
new nestedservletexception("handler processing failed", err));
}
finally {
// 9. 异步处理判断;文件上传请求则清理临时资源
if (asyncmanager.isconcurrenthandlingstarted()) {
if (mappedhandler != null) {
mappedhandler.applyafterconcurrenthandlingstarted(processedrequest, response);
}
}
else {
if (multipartrequestparsed) {
cleanupmultipart(processedrequest);
}
}
}
}3. 分步解析
| 步骤 | 方法 | 作用 |
|---|---|---|
| 1 | checkmultipart() | 判断是否是文件上传请求,是则提前解析 |
| 2 | gethandler() | 通过 handlermapping 找到 handler 和拦截器链 |
| 3 | gethandleradapter() | 找到能执行该 handler 的适配器 |
| 4 | applyprehandle() | 顺序执行拦截器的 prehandle |
| 5 | ha.handle() | 真正调用 controller 方法,返回 modelandview |
| 6 | applydefaultviewname() | 无视图名时生成默认视图名 |
| 7 | applyposthandle() | 逆序执行拦截器的 posthandle |
| 8 | processdispatchresult() | 异常处理、视图渲染、aftercompletion |
| 9 | finally | 异步判断、文件上传临时资源清理 |
- ① checkmultipart 文件上传解析
processedrequest = checkmultipart(request);
如果请求是 multipart/form-data 类型,就会交给 multipartresolver 解析,把原始请求包装成能读取上传文件的请求对象。这也和前面配置的 multipartresolver 组件对应上了。
- ② gethandler 查找处理器
mappedhandler = gethandler(processedrequest);
gethandler() 内部会遍历所有 handlermapping,找到能处理该请求的 handler,并返回一个 handlerexecutionchain。这个执行链中不仅包含目标 handler,还包含匹配到的拦截器。
- ③ gethandleradapter 匹配适配器
handleradapter ha = gethandleradapter(mappedhandler.gethandler());
handler 有多种形式(@requestmapping 方法、实现 controller 接口等),每种handler 需要对应的 adapter 来调用。gethandleradapter() 就是遍历handleradapter,找到支持当前 handler 的那一个。
- ④ applyprehandle 拦截器预处理
if (!mappedhandler.applyprehandle(processedrequest, response)) {
return;
}
顺序执行拦截器的 prehandle 方法,任何一个返回 false,请求就到此为止,不再执行后续流程。
- ⑤ ha.handle 执行处理器
mv = ha.handle(processedrequest, response, mappedhandler.gethandler());
这是真正执行业务逻辑的地方。对于 requestmappinghandleradapter,它会通过反射调用 controller 中的目标方法,最终返回 modelandview。
- ⑥⑦ 视图名与拦截器后处理
applydefaultviewname(processedrequest, mv); mappedhandler.applyposthandle(processedrequest, response, mv);
如果 handler 返回的 modelandview 没有视图名,会先生成默认视图名;然后逆序执行拦截器的 posthandle。
- ⑧ processdispatchresult 处理结果
processdispatchresult(processedrequest, response, mappedhandler, mv, dispatchexception);
这一步负责收尾,简化后的逻辑如下:
private void processdispatchresult(httpservletrequest request, httpservletresponse response,
handlerexecutionchain mappedhandler, modelandview mv, exception exception) throws exception {
// 1. 如果执行过程发生异常,交给异常解析器处理
if (exception != null) {
object handler = (mappedhandler != null ? mappedhandler.gethandler() : null);
mv = processhandlerexception(request, response, handler, exception);
}
// 2. 渲染视图
if (mv != null && !mv.wascleared()) {
render(mv, request, response);
}
// 3. 执行拦截器的 aftercompletion
if (mappedhandler != null) {
mappedhandler.triggeraftercompletion(request, response, null);
}
}这里有两个关键点:
- processhandlerexception() 会交给
handlerexceptionresolver处理异常,这就是后面"异常处理机制"的底层入口。 triggeraftercompletion()无论正常还是异常,都会逆序执行拦截器的aftercompletion。
三. spring mvc 异常处理机制
3.1 springmvc 异常的处理流程
异常分为编译时异常和运行时异常,编译时异常我们 try-catch 进行捕获,捕获后自行处理,而运行时异常是不可预期的,就需要规范编码来避免,在springmvc 中,不管是编译异常还是运行时异常,都可以最终由springmvc提供的异常处理器进行统一处理,这样就避免了随时随地捕获处理的繁琐性。
当然除了繁琐之外,我们在进行前后端分离异步开发时,往往返回统一格式的结果给客户端,例如:
{“code”:200,“message”:“”,“data”:{“username”:“haohao”,“age”:null}},即使报异常了,也不能把状态码500直接扔给客户端丢给用户,需要将异常转换成符合上面格式的数据响应给客户端更友好。

3.2 springmvc 的异常处理方式
springmvc 提供了以下三种处理异常的方式:
- 简单异常处理器:使用springmvc 内置的异常处理器处理 simplemappingexceptionresolver;
- 自定义异常处理器:实现handlerexceptionresolver接口,自定义异常进行处理;
- 注解方式:使用@controlleradvice + @exceptionhandler 来处理。
1. 简单异常处理器 simplemappingexceptionresolver
simplemappingexceptionresolver 是 spring mvc 内置的异常处理器,作用是把指定的异常映射到对应的错误页面,适合传统 jsp 项目。
@configuration
public class springmvcconfig implements webmvcconfigurer {
@override
public void configurehandlerexceptionresolvers(list<handlerexceptionresolver> resolvers) {
simplemappingexceptionresolver resolver = new simplemappingexceptionresolver();
properties mappings = new properties();
mappings.setproperty("java.lang.nullpointerexception", "nullerror");
mappings.setproperty("java.lang.arithmeticexception", "aritherror");
resolver.setexceptionmappings(mappings);
// 未匹配到的异常使用默认错误页
resolver.setdefaulterrorview("error");
resolvers.add(resolver);
}
}- exceptionmappings 的 key 是异常类的全限定名,value 是对应的错误页面视图名。
- 这种方式只能跳转到错误页面,不能直接返回 json,主要用在传统页面开发场景。
- 注意:
configurehandlerexceptionresolvers()一旦添加了自定义解析器,spring mvc 就不会再添加默认解析器;如果只是想追加,应该使用extendhandlerexceptionresolvers()。
2. 自定义异常处理器
实现 handlerexceptionresolver 接口,可以完全自定义异常处理逻辑。
@component
public class myhandlerexceptionresolver implements handlerexceptionresolver {
@override
public modelandview resolveexception(httpservletrequest request, httpservletresponse response,
object handler, exception ex) {
modelandview modelandview = new modelandview();
if (ex instanceof businessexception) {
modelandview.addobject("msg", ex.getmessage());
modelandview.setviewname("bizerror");
} else {
modelandview.addobject("msg", "系统繁忙,请稍后重试");
modelandview.setviewname("error");
}
return modelandview;
}
}- 返回 null 表示当前解析器不处理该异常,spring mvc 会继续交给下一个异常解析器。
- 可以根据异常类型分别跳转到不同页面,比 simplemappingexceptionresolver 更灵活。
- 缺点是返回的是 modelandview,不适合直接返回 json 的场景。
3. 注解方式 @controlleradvice + @exceptionhandler
这是目前最主流的方式,配合统一返回类,可以把异常转换成 json 返回给前端。
@restcontrolleradvice
public class globalexceptionhandler {
// 处理自定义业务异常
@exceptionhandler(businessexception.class)
public result handlebusinessexception(businessexception e) {
return result.error(e.getcode(), e.getmessage());
}
// 兜底处理其他未知异常
@exceptionhandler(exception.class)
public result handleexception(exception e) {
return result.error("系统繁忙,请稍后重试");
}
}统一返回类:
public class result {
private integer code;
private string message;
private object data;
public static result success(object data) {
result result = new result();
result.code = 200;
result.message = "";
result.data = data;
return result;
}
public static result error(integer code, string message) {
result result = new result();
result.code = code;
result.message = message;
return result;
}
// getter/setter 省略
}自定义业务异常:
public class businessexception extends runtimeexception {
private integer code;
public businessexception(integer code, string message) {
super(message);
this.code = code;
}
public integer getcode() {
return code;
}
}- @restcontrolleradvice 等价于 @controlleradvice + @responsebody,方法返回值会直接序列化成 json。
- 当有多个 @exceptionhandler 方法时,spring会选择异常类型最匹配的那个。
- 业务代码只需要正常抛出异常,由全局处理器统一转换成前端需要的格式。
3.3 异常处理机制原理剖析
回顾 dodispatch() 的主流程:当 handler 执行抛出异常后,会被 catch 捕获并保存到 dispatchexception,然后统一交给 processdispatchresult() 处理。processdispatchresult() 发现异常后,会调用 processhandlerexception(),这就是异常处理机制的核心入口。
protected modelandview processhandlerexception(httpservletrequest request, httpservletresponse response,
object handler, exception ex) throws exception {
modelandview exmv = null;
// 遍历所有 handlerexceptionresolver
if (this.handlerexceptionresolvers != null) {
for (handlerexceptionresolver resolver : this.handlerexceptionresolvers) {
exmv = resolver.resolveexception(request, response, handler, ex);
if (exmv != null) {
break; // 只要有解析器处理了,就结束循环
}
}
}
if (exmv != null) {
if (exmv.isempty()) {
request.setattribute(exception_attribute, ex);
return null;
}
// 没有视图名时,生成默认视图名
if (!exmv.hasview()) {
string defaultviewname = getdefaultviewname(request);
if (defaultviewname != null) {
exmv.setviewname(defaultviewname);
}
}
return exmv;
}
// 所有解析器都返回 null,异常继续向上抛出
throw ex;
}3.4 springmvc 常用的异常解析器
spring mvc 默认会注册以下几个异常解析器,并按顺序执行:
| 异常解析器 | 作用 | 关联注解/类 |
|---|---|---|
exceptionhandlerexceptionresolver | 处理 @exceptionhandler、@controlleradvice | @exceptionhandler |
responsestatusexceptionresolver | 处理带 @responsestatus 的异常 | @responsestatus |
defaulthandlerexceptionresolver | 处理 spring mvc 内部异常,转成 http 状态码 | spring 内置异常 |
simplemappingexceptionresolver | 把异常映射到错误页面 | 手动配置 |
1. exceptionhandlerexceptionresolver
它是优先级最高的默认解析器,负责处理:
- controller 中的 @exceptionhandler 方法;
- @controlleradvice / @restcontrolleradvice 中的 @exceptionhandler 方法。
内部通过 exceptionhandlermethodresolver 把"异常类型"和"处理方法"建立映射,当异常发生时,找到最匹配的方法反射调用。前面 3.2 讲的注解方式,底层就是由它完成。
2. responsestatusexceptionresolver
它负责处理带有 @responsestatus 注解的异常。例如:
@responsestatus(value = httpstatus.not_found, reason = "资源不存在")
public class resourcenotfoundexception extends runtimeexception {
}
当业务代码抛出 resourcenotfoundexception 时,responsestatusexceptionresolver 会捕获它,并把响应状态码设置为 404,提示信息设置为 reason 中的内容。
3. defaulthandlerexceptionresolver
它负责处理 spring mvc 内部抛出的各类异常,把它们转换成对应的 http 状态码。常见映射如下:
| 异常 | 状态码 |
|---|---|
nohandlerfoundexception | 404 |
httprequestmethodnotsupportedexception | 405 |
httpmediatypenotsupportedexception | 415 |
missingservletrequestparameterexception | 400 |
typemismatchexception | 400 |
httpmessagenotreadableexception | 400 |
methodargumentnotvalidexception | 400 |
asyncrequesttimeoutexception | 503 |
例如,请求方法不支持时抛出 httprequestmethodnotsupportedexception,最终客户端就会收到 405 method not allowed。
4. simplemappingexceptionresolver
这个解析器不会默认注册,需要手动配置。它通过配置"异常类 → 错误页面"的映射关系来处理异常,在 3.2 的"方式一"中已经详细讲解过,这里不再重复。
到此这篇关于spring mvc核心组件与原理深度剖析的文章就介绍到这了,更多相关spring mvc核心组件内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论