1、Web 开发中的请求-响应模型
我们都知道在Web世界里都是
1、客户端浏览器发起请求;
2、服务器接收、处理并响应(一般为HTML页面);
3、客户端浏览器对接收的内容进行视图渲染。
2、Web MVC 模型
MVC模型:是模型、视图、控制器的简写,是一种架构模型,是一种软件设计规范,是开发的最佳实践。本身并不引入新功能,只是帮助我们将开发的组织的更加合理。使模型、视图、控制相分离,责任分离。
-
Model(模型):数据模型,提供要显示的数据,包含数据和行为。比如JavaBean组件。通常又分为数据模型和业务逻辑模型,数据模型用来存放业务数据;业务逻辑模型包含应用的业务方法。
-
View(视图):用户界面,将模型的内容展现给用户
-
Controller(控制器):接受用户请求,委托给模型进行处理(状态改变),处理完成后将模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
典型的MVC模型就是JSP+Servlet+JavaBean
3、Spring MVC 是什么?
Spring MVC 是Spring Framework的一部分,是一种基于Java且实现了Web MVC架构的请求驱动类型的轻量级Web框架。即使用MVC架构模式的思想,将web层进行职责解耦,基于请求驱动是指使用请求-响应模型。框架的目的就是帮助我们简化开发,Spring MVC同样如此。
Spring MVC 作用:
- 帮助我们进行更简洁的Web层开发
- 与Spring框架无缝集成
- 提供约定大于配置的契约式编程
- 支持RestFul风格
- 解决WEB开发中常见的问题(参数解析、文件上传、表单验证、国际化、等等)
4、四大组件
整个处理流程用到的组件有前端控制器 DispatcherServlet
、处理器映射器 HandlerMapping
、处理器适配器 HandlerAdapter
、处理器 Handler
、视图解析器 ViewResolver
、视图 View
。
在介绍四大组件前,先介绍一下Handler
。Handler
是一个术语,不是接口,有多种实现方式,例如基于注解@Controller
的处理器,实现HttpRequestHandler
接口的处理器,和实现Controller
接口的处理器。因为实现方式不一样,调用方式也就不确定了。所以需要不同的HandlerMapping
来映射不同的Handler
,而且通过处理器适配器HandlerAdapter
将处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。
-
HandlerMethod
:它是对标注@Controller
注解的Bean本身和请求Method的包装 -
实现了
HttpRequestHandler
接口的ResourceHttpRequestHandler
:用于处理静态资源请求
四大组件的作用:
-
前端控制器
DispatcherServlet
:接收用户请求,调用其他三大组件进行处理,完成后进行响应。前端控制器作为统一的访问节点,相当于转发器或中央处理器,进行全局的流程控制,对各个组件进行调度,降低组件之间的耦合性,有利于组件之间的扩展。 -
处理器映射器
HandlerMapping
:请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain
对象【包含一个Handler
处理器对象、多个HandlerInterceptor
拦截器】 -
处理器适配器
HandlerAdapter
:HandlerAdapter
将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。 -
视图解析器
ViewResolver
:把逻辑视图名解析为具体的视图对象view
,通过这种策略模式,很容易更换其他视图技术。
5、执行流程
执行流程:
1、用户请求到达DispatcherServlet类
进行处理
2、DispatcherServlet类
遍历所有配置的HandlerMapping类
请求找到Handler
3、HandlerMapping类
根据request请求
的URL
等信息查找到了能够进行处理的Handler
,并和相关拦截器interceptor
并构造HandlerExecutionChain
返回给前端控制器DispatcherServlet类
4、DispatcherServlet类
通过HandlerExecutionChain
拿到保存的Handler
,遍历所有配置的HandlerAdapter类
请求执行Handler
。然后将支持当前Handler
的HandlerAdapter
返回给前端控制器DispatcherServlet类
5、HandlerAdapter类
执行相关Handler
并获取ModelAndView类
,然后将此对象返回给前端控制器【ModelAndView
包含模型数据、逻辑视图名】
6、DispatcherServlet类
遍历所有配置的ViewResolver类
请求进行视图解析
7、ViewResolver类
把逻辑视图名解析为具体的视图对象View
,然后返回给向前端控制器
8、View类
会根据传进来的Model模型数据进行渲染。将 Model
中的模型数据填充到 View
视图中的 request
域,生成最终的视图 View
9、DispatcherServlet类
向用户返回响应。
从上我们可以发现,SpringMVC
对于用户的请求是以DispatcherServlet类
为核心,并与另外三大组件HandlerMapping
、HandlerAdapter
以及ViewResolver
进行交互。三大组件之间没有交互并且相互解耦,符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。因此三大组件可以替换不同的实现而没有任何影响。
核心开发步骤:
1、 DispatcherServlet
在web.xml中的部署描述,从而拦截请求到Spring MVC
2、 HandlerMapping
的配置,从而将请求映射到处理器
3、 HandlerAdapter
的配置,从而支持多种类型的处理器
4、 ViewResolver
的配置,从而将逻辑视图名解析为具体视图技术
5、处理器(页面控制器)的配置,从而进行功能处理
6、源码
DispatcherServlet
实际是一个的Servlet
(它继承自HttpServlet
基类)。请求经过分发,最终会来到这个方法org.springframework.web.servlet.DispatcherServlet#doDispatch,因此执行流程也从此方法开始。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//用户的request请求
HttpServletRequest processedRequest = request;
//HandlerExecutionChain局部变量
HandlerExecutionChain mappedHandler = null;
//判断是否解析了文件类型的数据,如果有最终需要清理
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//ModelAndView局部变量
ModelAndView mv = null;
//处理异常局部变量
Exception dispatchException = null;
try {
//检查请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
mappedHandler = getHandler(processedRequest);
//如果HandlerExecutionChain为null,请求到处理器的映射失败,抛出异常
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//根据查找到的Handler请求查找能够进行处理的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 304 Not Modified缓存支持 判断自上次请求后是否有修改,没有修改直接返回响应
略
//执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//由适配器执行处理器(调用处理器相应功能处理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
//执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//渲染视图填充Model,如果有异常渲染异常页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//如果有异常按倒序执行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//如果有异常按倒序执行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
//倒序执行所有HandlerInterceptor的afterCompletion方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
//如果请求包含文件类型的数据则进行相关清理工作
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch()
方法通过调用getHandler()
方法并传入request
,通过遍历HandlerMapping
找到Handler
和相关拦截器HandlerInterceptor
构造HandlerExecutionChain
然后返回该对象。源码如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
如果通过HandlerMapping
找能具体Handler
,以及相关拦截器interceptor
并构造HandlerExecutionChain
对象返回。比如通过RequestMappingHandlerMapping
将请求映射到自己写的Controller里标注@RequestMapping
的方法上。
接下来会调用getHandlerAdapter()
方法来查找能够对Handler
进行处理的HandlerAdapter
,查看其源码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
查找到了对应的HandlerAdapter
后就会调用HandlerExecutionChain
的applyPreHandle()
方法来执行配置的所有HandlerInteceptor
的preHandle()
方法,查看其源码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
HandlerExecutionChain
的applyPreHandle()
方法会按照顺序依次调用HandlerInterceptor
的preHandle()
方法,但是如果任一HandlerInterceptor
的preHandler()
返回了false
就不再继续执行其它拦截器。而是执行triggerAfterCompletion()
方法,查看该方法源码如下:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
这里遍历的下标为interceptorIndex
,该变量在前一个方法applyPreHandle()
方法中赋值,如果preHandle()
方法返回true
该变量加一,因此该方法会逆序执行所有preHandle()
方法返回了true
的HandlerInterceptor
的afterCompletion()
方法。
继续阅读doDispatch()
方法的源码,如果所有拦截器的preHandle()
方法都返回了true
没有进行拦截,接下来前端控制器会请求执行上文获取的Handler
,这个Handler
就是开发的时候编写的Controller
,根据实现接口的不同执行相关方法,并获取到ModelAndView类
的对象。
接下来会逆序执行HandlerInterceptor
的postHandle()
方法,具体源码如下:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
执行完postHandle()
方法后,doDispatch()
方法调用了processDispatchResult()
方法,其源码如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//判断HandlerMapping、HandlerAdapter处理时的异常是否为空
if (exception != null) {
//上述两个组件处理时的异常不为空
//如果为ModelAndViewDefiningException异常,则获取一个异常视图
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
//如果不为ModelAndViewDefiningException异常,进行异常视图的获取
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); //处理异常
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
//判断mv是否为空,不管是正常的ModelAndView还是异常的ModelAndView,只要存在mv就进行视图渲染
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
//否则记录无视图
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//执行相关HandlerInterceptor的afterCompletion()方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
该方法传入了一个异常类的对象dispatchException
,阅读doDispatch()
方法的源码可以看出,Spring MVC
对整个doDispatch()
方法用了嵌套的try-catch
语句,内层的try-catch
用于捕获HandlerMapping
进行映射查找HandlerExecutionChain
以及HandlerAdapter
执行具体Handler
时的处理异常,并将异常传入到上述processDispatchResult()
方法中。
如果有异常,则遍历所有的处理器异常解析器HandlerExceptionResolver
看谁能够处理异常,如果你使用了@ControllerAdvice+@ExceptionHandler注解配置了全局异常处理,那么该异常会被处理,指定了错误逻辑错误视图后返回ModelAndView
,进入视图渲染流程。如果该异常没能够被任何处理器异常解析器处理,就会抛出异常,由Tomcat发送 /error
请求,被HandlerMapping
映射到BasicErrorController
处理,进入默认的错误处理流程。最终也是返回ModelAndView
。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
//1、遍历所有的处理器异常解析器handlerExceptionResolvers,看谁能够处理当前异常
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
//解析当前异常
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
//ModelAndView 不为空时,说明解析成功
if (exMv != null) {
break;
}
}
//下面是当exMv不为空时,说明异常解析器解析成功,但是我们暂时只考虑,所有处理器异常解析器都不能处理该异常
...略
...略
...略
//所有处理器异常解析器都不能处理该异常,抛出异常
throw ex;
}
接着不管视图是正常视图还是异常视图,只要ModelAndView
不为空,均调用render()
方法进入视图渲染流程,查看render()
方法的具体源码如下:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
// 解析视图名称获取对应视图View
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
//如果视图View为空抛出异常
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
//设置Http响应状态字
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//调用视图View的render方法通过Model来渲染视图
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
render()
方法通过调用resolveViewName()
方法根据视图逻辑名称解析对应的视图对象View
,该方法源码如下:
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
resolveViewName()
方法通过遍历配置的所有ViewResolver类
根据视图名称来解析对应的视图View
,如果找到则返回对应视图View
,没有找到则返回null
。
回到前一个render()
方法,如果上述方法返回的视图为null
则抛出异常,这个异常相信大多数人也见过,当开发时写错了返回的View
视图名称时就会抛出该异常。接下来调用具体视图的render()
方法来进行Model
数据的渲染填充,最终构造成完整的视图。
到这里,doDispatch()
的外层try-catch
异常的作用我们就知道了,为了捕获渲染视图时的异常,通过两层嵌套的try-catch
,Spring MVC
就能够捕获到三大组件在处理用户请求时的异常,通过这样的方法能够很方便的实现统一的异常处理。