一、MVC程序

我们使用全注解的方式来使用MVC,所以先写段演示程序。

/**
* 系统初始化入口。【代替web.xml】
*/
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { /**
* Spring配置(设置spring容器启动的入口。配置ContextLoaderListener)
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{AppConfig.class};
} /**
* SpringMVC配置文件。(配置DispatcherServlet)
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebMvcConfig.class};
} /**
* 设置DispatcherServlet的拦截路径
*/
@Override
protected String[] getServletMappings() {
// /:拦截所有请求,包括静态资源,但不包括jsp
// /*:拦截所有请求,包括静态资源和jsp文件
return new String[]{"/"};
}
} /**
* Spring父容器扫描.【@Contoller注解由MVC子容器来扫描,MVC中的C就表示Controller】
*/
@ComponentScan(value = "com.mvc", excludeFilters = {//排除扫描@Controller
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
})
public class AppConfig {
} /**
* SpringMVC子容器
*/
@EnableWebMvc
//@ComponentScan("com.mvc")
@ComponentScan(value = "com.mvc", includeFilters = {//扫描@Controller
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)//禁用默认的过滤规则
public class WebMvcConfig extends WebMvcConfigurerAdapter { /**
* 继承默认的转换器。【导入jackson包后会自动添加json转换器,不需显式添加】
*/
/*
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
System.out.println(converters);
converters.add(new MappingJackson2HttpMessageConverter());
System.out.println(converters);
}*/ /**
* 拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyAuthInterceptor());
} /**
* 视图解析器
*/
@Bean
public ViewResolver viewResolver() {
//jsp视图解析器
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
} /**
* 配置静态资源的处理
* 将请求交由Servlet处理,不经过DispatchServlet
*/
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
} } /**
* 返回视图
*/
@Controller
public class UserController { @Autowired
private UserService userService; @GetMapping("/user")
public String getUser() {
userService.getUser();
return "success";
} } /**
* 返回rest数据
*/
@RestController
public class UserRestController { @Autowired
private UserService userService; @ResponseBody
@GetMapping("/user-rest")
public User getUser() {
return userService.getUser();
} }

二、源码分析

1.MVC工作原理

下面是DispatcherServlet的工作原理图,图片来源于网络。

DispatcherServlet的工作流程如下:

1.请求到达后,调用HandlerMapping来查找对应的处理器Handler

2.查找能调用上面Handler的HandlerAdapter。(并非直接调用Handler,而是使用的适配器模式)

3.预处理操作。比如执行拦截器

4.真正的处理请求的操作。(比如执行controller中的某方法来处理请求)

5.后处理操作。重走拦截器栈。(跟strut2中的拦截器栈相似)

6.进行视图渲染输出。如果前面步骤发生了异常,则这里要处理异常。

    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 {
          //0.检查是否为文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // 1.查找能处理当前请求的处理器 Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // 2.查找能处理当前请求的处理器适配器 Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
          // 3.预处理:比如调用拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // 4.真正的处理请求(调用Controller的处理方法) Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
          // 5.处理后操作:比如再次重走拦截器栈
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
       // 6.处理最终结果:返回视图或处理异常
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

下面来看看每个步骤的源码

1.查找处理器-getHandler

遍历处理器HandlerMapping,查找出能够处理该请求的处理器,显然可以是多个,这些处理器组成了一个处理器执行链。

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍历SpringMVC内置的几种HandlerMapping
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
} HandlerExecutionChain handler = hm.getHandler(request);
//只要找到一种能处理我们的程序的HandlerMapping就返回。
//
if (handler != null) {
return handler;
}
}
return null;
}

由于我们可以使用不同的方式来开发SpringMVC应用,每种方式SpringMVC都能找到对应的处理器来进行处理。比如我们使用@RequestMapping注解方式,则SpringMVC就利用RequestMappingHandler来处理。常见的几种HandlerMapping有RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMaping。

SpringMVC只会选用其中一种HandlerMapping来处理。因为我们编写的程序使用了@RequestMapping,实际上就是@RequestMapping方式,所以SpringMVC对应使用RequestMappingHandlerMapping来处理。

SpringMVC是怎么判断RequestMappingHandlerMapping能处理我们的程序呢?实际上每种HandlerMapping对应一些默认的拦截器,也可能没有对应的拦截器。SpringMVC通过查找所有的拦截器,如果拦截器能够处理当前请求,则将这些拦截器串起来组成拦截器链。只要该拦截器链不为null,就表明能够处理我们的程序。

    @Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//获取能处理当前Controller的拦截器链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
} //AbstractHandlerMapping#getHandlerExecutionChain()
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
//查找所有能处理当前url的拦截器,组成拦截器链
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

下面是能处理我们UserController的拦截器链,除了我们自定义的MyAuthInterceptor外,还有两个默认拦截器。

2.查找能调用Handler的HandlerAdapter

因为系统中有不同类型的controller,每种controller的使用方式都不一样。必然会出现这样的代码。

if(handler instanceof A类型){

  //处理A类型的controller

}else (handler instanceof B类型){  

  //处理B类型的controller

}

显然这样的处理方式不利于扩展,因此使用适配器模式来调用不同的处理器。

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍历所有的HandlerMethodAdapter
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//只要该HandlerMethodAdapter支持处理handler就返回
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");
} //AbstractHandlerMethodAdapter.supports()
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
} //RequestMappingHandlerAdapter.supportsInternal()
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
//RequestMappingHandlerAdapter默认是支持处理handler的
return true;
}

RequestMappingHandlerAdapter支持处理我们程序中的UserController。

3.预处理操作

在处理请求之前会先执行预处理操作,比如执行拦截器。具体逻辑是遍历拦截器栈,找出能处理该请求的拦截器(每个拦截器都要配置拦截映射),然后调用该拦截器的preHandler()方法进行前置处理。在之后的第5步会逆序重走拦截器栈进行后置处理(同struts2)。

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;
}

4.真正的处理请求

这里会真正的执行controller中的方法来处理请求。下面来看看我们常用的RequestMappingHandlerAdatper是如何处理请求的。

    @Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
} protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { checkRequest(request); if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
} // Execute invokeHandlerMethod in synchronized block if required.
     //同步调用
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {//加锁
            //调用controller中的方法
return invokeHandlerMethod(request, response, handlerMethod);
}
}
}
     //非同步调用
return invokeHandlerMethod(request, response, handlerMethod);
}

5.执行后处理操作

第3步已经提到了,这里会逆序重新走一遍拦截器栈,也就是调用拦截器的postHandler()方法进行后置处理。

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);//后置处理
}
}
}

6.视图渲染和异常处理

这一步主要是进行视图的渲染,如果前面步骤有异常,这里就进行异常处理。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; //发生了异常,则处理异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
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?
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;
} if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

处理异常的过程如下:

遍历异常解析器来解析异常。通常我们会配置SpringMVC的全局异常解析器来进行统一异常处理,就是在这里使用它来处理异常的。

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
} throw ex;
}

DispatcherServlet的工作原理的更多相关文章

  1. Spring MVC中DispatcherServlet工作原理探究

    转:http://blog.csdn.net/zhouyuqwert/article/details/6853730 下面类图将主要的类及方法抽离出来,以便查看方便,根据类的结构来说明整个请求是如何工 ...

  2. [Java] SpringMVC工作原理之一:DispatcherServlet

    一.DispatcherServlet 处理流程 在整个 Spring MVC 框架中,DispatcherServlet 处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应工作.在看 Di ...

  3. springMVC 的工作原理和机制

    工作原理上面的是springMVC的工作原理图: 1.客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web. ...

  4. ssh框架的工作原理

    struts2的工作原理 1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求 2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextClea ...

  5. SpringMVC框架的工作原理

    学习SpringMVC的工作原理,首先有三个要解决的问题: (1)DispathcherServlet框架如何截获特定的HTTP请求,交由SpringMVC处理? (2)位于Web层的Spring容器 ...

  6. springMVC 的工作原理和机制(转)

    工作原理上面的是springMVC的工作原理图: 1.客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web. ...

  7. SSH三大框架的工作原理及流程

    Hibernate工作原理及为什么要用? 原理:1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件2.由hibernate.cfg.x ...

  8. springmvc工作原理以及源码分析(基于spring3.1.0)

    springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...

  9. Hibernate工作原理及为什么要用?

    Hibernate工作原理及为什么要用? 原理:1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件2.由hibernate.cfg.x ...

随机推荐

  1. React-Native 之 GD (十七)小时风云榜按钮处理

    小时风云榜按钮处理 在服务器返回给我们的 json 数据中,提供了 hasnexthour 字段,当这个字段返回为 1 的时候,表示后面还有内容,按钮可以点击,否则不能点击,按照这个思路,我们就来完成 ...

  2. Qualcomm 8X camera过程解析【转】

    本文转载自:http://blog.csdn.net/gabbzang/article/details/19906687 http://www.01yun.com/mobile_development ...

  3. dp培训完结(8.9)

    概率与期望dp 期望: 为什么下面的式子成立? 若x可以取1,2,3,则x+c可以取1+c,2+c,3+c..........x*c可以取1*c,2*c,3*c why? 举个例子(E(x+y)=E( ...

  4. fedora23使用Xwayland的gnome-shell

    gnome是桌面管理系统的名称, 包括gnome, kde, xfce等等 同时, gnome是旧的gnome 2 的桌面管理 在gnome 3中, 桌面管理系统叫做gnome shell. gnom ...

  5. 关于 token

    用户在浏览器做一系列操作,后台服务怎么判断这些操作是来自同一个用户? 1. seesion 用户登录后,后台生成 sessionid 返回给浏览器,浏览器的每次请求带上 sessionid,后台关联 ...

  6. Delphi XE2 之 FireMonkey 入门(8) - TImage

    TImage 主要成员: { 属性 } Bitmap              : TBitmap;        //图像 BitmapMargins        : TBounds;      ...

  7. 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第1节 Scanner类_1-API概述和使用步骤

    官方翻译的中文版本

  8. UI自动化之8种基础定位

    UI自动化的核心在于定位 目录 1.8种基础定位方法 2.xpath定位 3.css定位 4.多组元素 1.8种基础定位方法 driver.find_element_by_id() #id定位 dri ...

  9. Fiddler代理抓取的接口的服务器返回出现"Response body is encoded. Click to decode. "

    参考与:https://blog.csdn.net/wsbl52006/article/details/53256705 解决办法: Rules > Remove All Encodings 勾 ...

  10. python基础-4.1 open 打开文件练习:修改haproxy配置文件

    1.如何在线上环境优雅的修改配置文件? 配置文件名称ini global log 127.0.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 in ...