【深入学习Spring】——SpringMVC工作原理
先写段演示程序,以注解的方式来使用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(); } }
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 { 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; }
由于我们可以使用不同的方式来开发SpringMVC应用,每种方式SpringMVC都能找到对应的处理器来进行处理。比如我们使用@RequestMapping注解方式,则SpringMVC就利用RequestMappingHandler来处理。常见的几种HandlerMapping有RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMaping。SpringMVC只会选用其中一种HandlerMapping来处理。
由于我们编写的程序使用了@RequestMapping注解,所以会使用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接口的方式,可以使用注解的方式,等等。所以系统中有不同类型的controller,每种实现方式必然对应不同的处理逻辑,所以必然会出现这样的代码。
if(handler instanceof A类型){ //处理A类型的controller }else (handler instanceof B类型){ //处理B类型的controller }
显然这样的处理方式不利于扩展,因此SpringMVC使用了适配器模式。
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; }