关注「Java视界」公众号,获取更多技术干货

Spring MVC调度流程

一、Spring MVC的由来

 在最早期WEB系统有前端到后端是:

请求+JSP+Java Bean + 数据库 的模式,这种JSP和Java Bean前后端耦合在一起会带来很多麻烦,这种模式下JSP身兼多职,既要负责视图层的数据展示,又要负责业务流程控制,结构较为混乱,也不是我们所希望的松耦合架构,如下:

后来被Servlet+JSP+Java Bean+数据库 的模式取代,多了一个Serverlet组件,用户的请求先到Servlet,Servlet相当于一个控制器,它来调度Java Bean以及读取数据库,最后把结果放到JSP进行展示。这时Servlet就是Controller,Java Bean就是Model,JSP就是View,这时前端和后端是解耦的,这时MVC分层架构的由来。当业务流程比较复杂的时候,就需要把业务流程控制交给专门的控制器,JSP只专注于视图的渲染展现即可。但是随着移动端手机、平板、各种终端的请求增加,这种模式的短板也暴露出来,那就是JSP适用于WEB端,但是不支持移动端。

为了兼顾各种终端,Spring MVC框架在上面的基础上进行了调整。它将原来的Model层又拆分成Service层和Dao层,如下:

Spring MVC是Spring家族中应用于Web应用的一个模块,是Spring提供的一个基于MVC设计模式的Web开发框架,可以将它理解为Servlet。在MVC模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互,是结构最清晰的JSP Model2实现,可以说是一个典型的MVC框架。

除此之外,Spring MVC框架采用松耦合、可插拔的组件结构,具有高度可配置性,比起其他的MVC框架更具有扩展性和灵活性。并且它本身就是Spring家族的一部分,与Spring框架整合更是天衣无缝。

在Spring MVC框架中,Controller替换Servlet来担负控制器的职责。Controller接收请求,调用相应的Model进行处理,Model处理完之后将结果返回给Controller,Conreoller再指派相应的View对处理结果进行渲染,最终响应给客户端进行展示。
 

SpringMVC Spring 关系是啥?

SpringMVC 是 Spring 的部分,Spring 在原有基础上,又提供了web应用的mvc模块。

所以SpringMVC 是基于 Spring 框架派生 Web 框架, 所以它天然就可以十分方便的整合到 Spring 框架中,而 Spring 整合 Struts2 还是比较繁复的(另外Struts2 还爆出了漏洞问题)。

SpringMVC  开始就定位为松散的组合,展示给用户的视图(View)、 控制器返回的数据模型( Model )、 定位视图的视图解析器(ViewResolver) 和处理适配器( HandlerAdapter )等都是独立的,换句话说, Spring MVC 很容易把后台数据转换为各种类型的数据 ,以满足互联网多样化的要求, 例如,Spring MVC 可以十分方便的转换为目前最常用 JSON 数据集,也可以转换为 PDF、Excel 和XML等。

下面我们需要研究下它的转换流程。

二、Spring MVC的组件与流程

流程和组件是 SpringMVC 的核心, Sp ringMVC 的流程是围 Dispatche rServlet 而工作的,所以  DispatcherServlet 是其最重要的 内容。在DispatcherServlet的基础上,还存在其他的组件, 掌握 流程和组件就是 SpringMVC 开发的 基础。总体流程图如下:

这个图不知道谁画的,画的还挺明白我就盗过来了,不过还是要解释一下:

  • ⑴ 用户发送请求至DispatcherServlet。
  • ⑵ DispatcherServlet收到请求调用HandlerMapping查询具体的Handler。
  • ⑶ HandlerMapping找到具体的处理器(具体配置的是哪个处理器的实现类),生成处理器对象及处理器拦截器(HandlerExcutorChain包含了Handler以及拦截器集合)返回给DispatcherServlet。
  • ⑷ DispatcherServlet接收到HandlerMapping返回的HandlerExcutorChain后,调用HandlerAdapter请求执行具体的Handler(Controller)。
  • ⑸ HandlerAdapter经过适配调用具体的Handler(Controller即后端控制器)。
  • ⑹ Controller执行完成返回ModelAndView(其中包含逻辑视图和数据)给HandlerAdaptor。
  • ⑺ HandlerAdaptor再将ModelAndView返回给DispatcherServlet。
  • ⑻ DispatcherServlet请求视图解析器ViewReslover解析ModelAndView。
  • ⑼ ViewReslover解析后返回具体View(物理视图)到DispatcherServlet。
  • ⑽ DispatcherServlet请求渲染视图(即将模型数据填充至视图中) 根据View进行渲染视图。
  • ⑾ 将渲染后的视图返回给DispatcherServlet。
  • ⑿ DispatcherServlet将响应结果返回给用户。

解释下上面的各组件: 

  • (1)前端控制器DispatcherServlet(配置即可)
  • 功能:中央处理器,接收请求,自己不做任何处理,而是将请求发送给其他组件进行处理。DispatcherServlet 是整个流程的控制中心。
  • (2)处理器映射器HandlerMapping (配置即可)
  • 功能:根据DispatcherServlet发送的url请求路径查找Handler
  • 常见的处理器映射器:BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,
  • ControllerClassNameHandlerMapping,DefaultAnnotationHandlerMapping(不建议使用)
  • (3)处理器适配器HandlerAdapter(配置即可)
  • 功能:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
  • 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展多个适配器对更多类型的处理器进行执行。
  • 常见的处理器适配器:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,AnnotationMethodHandlerAdapter
  • (4)处理器Handler即Controller(程序猿编写)
  • 功能:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
  • (5)视图解析器ViewReslover(配置即可)
  • 功能:进行视图解析,根据逻辑视图名解析成真正的视图。
  • ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
  • springmvc框架提供了多种View视图类型,如:jstlView、freemarkerView、pdfView...
  • (6)视图View(程序猿编写)
  • View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)
图2-1
图2-2

首先,在Web务器启动的过程中,如果在 Spring Boot 机制下 Spring MVC 它就开始初始化一些重要的组件,例如 DispactherServlet,RequestMappingHandlerAdapter(HandlerAdapter的实现类)等组件对象。具体的可以查看spring webmvc-xxx.jar 包的属性文件DispatcherServlet.properties, 它定义的对象都是在 SpringMVC 开始时就初始化,并且存放在 Spring IoC容器中,如图2-1和图2-2。

我们先看一个Controller,也就是控制器:

@Controller
@RequestMapping("/home")
public class TestController {
    @RequestMapping("/test1")
    public String index_1(){
        String str = "666";
        ModelAndView mv = new ModelAndView();
        mv.setViewName("jj");
        mv.addObject("str", str);
        return str;
    }
}

@Controller注解标注这是一个控制器,@RequestMapping("/home")代表控制器和请求路径的映射关系,会在服务启动时就被扫描到HandlerMapping中存储,用户发起的请求会被DispactherServlet接收,并通过URI请求HandlerMapping返回一个HandlerExcutionChain对象,这个对象包含了处理器Handler,Handler是对控制器Controller的包装,同时Handler还包含拦截器(interceptor)。

DispactherServlet从HandlerMapping中得到了Handler,下面就需要执行这个Handler,执行Handler由HandlerAdapter完成。为什么还需要一个HandlerAdapter才能执行Handler?那是因为请求的种类的不同(有普通 HTTP 请求、WebSocket 的请求、按 BeanName 的请求),得到的Handler类型也不同,因此需要一个统一的适配器去运行HandlerExecutionChain 对象包含的Handler。

Handler在执行时会调用Controller,通过模型层获取数据,对数据进行处理后,最后返回模型和视图(ModelAndView )对象给DispactherServlet。(Controller方法可能存在参数,那么Handler就可以读入 HTTP和上下文的相关参数,然后再传递给Controller方法。而在Controller方法执行完成返回后,Handler又可以通过配置信息对控制器的返回结果进行处理

DispactherServlet拿到ModelAndView 对象就会转发给视图解析器( ViewResolver )。上面的代码中,我们给视图取名为“jj”,如果我们在配置文件中配置了视图的前缀和后缀:

spring.mvc.view.prefix=/WEB-INF/jsp
spring.mvc.view.suffix=.jsp

那么 ViewResolver 就会定位到视图, 就是/WEB-INF/jsp/jj .jsp 作为我们的视图。

视图解析器定位到视图后,视图的作用是将数据模型( Mode )渲染,这样就能够响应用户的请求。这一步就是视图将数据模型渲染( View )出来,用来展示给用户查看。

以上就是整个springMVC的调度流程。

附上调试时的断点信息:

/**
 * 测试用Controller
 */

@RestController
@Slf4j
@RequestMapping("/test")
public class TestController {

    @GetMapping("/test1")
    public String test1 () {
        return "okokok";
    }
}
doDispatch是DispatcherServlet调度的核心方法,基于这个方法,我们追一下断点看看:
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 {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			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 {
			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);
				}
			}
		}
	}

①  Return the HandlerExecutionChain for this request. 从HandlerMapping中获取HandlerExecutionChain

 

 

可以看到 HandlerExecutionChain 包含了 HandlerMethod 对象及拦截器集合。HandlerMethod 对象就是我们测试的Controller里的 test1()方法。

 ② Return the HandlerAdapter for this handler object. 根据返回的HandlerExecutionChain获取HandlerAdapter

 有三个 HandlerAdapter ,实际匹配的是 RequestMappingHandlerAdapter

 ③ Use the given handler to handle this request. 

AbstractHandlerMethodAdapter 

 

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// 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) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

 

@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
posted @ 2022-06-25 14:02  沙滩de流沙  阅读(69)  评论(0编辑  收藏  举报

关注「Java视界」公众号,获取更多技术干货