Spring MVC 初识

  • Post author:
  • Post category:其他




Spring MVC 是什么

Spring MVC属于SpringFrameWork的后续产品,是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。




Spring MVC 架构





Spring MVC 的核心处理请求流程如下图:


注:为了更好的从总体上把握请求流程,上图对请求流程做了很多简化,忽略了处理器返回结果和前端控制器返回结果的中更多的处理过程。

1.用户发送的请求交给统一的

一个前端控制器


2.前端控制器接收到请求,根据请求信息(如URL)从



多个页面控制器

中选择相匹配的一个,将请求委托给其处理。



3.页面控制/处理器接收到请求后进行相应的业务处理,并最终将处理结果返回给前端控制器。



4.


前端控制器


根据处理器返回的结果获取视图并对视图进行渲染,最终得到处理结果。




5.前端控制器将最终的结果返回给用户。






前端控制器

在Spring MVC中前端控制器是DispatcherServlet

这是一个servlet,那么事情就很明显了——用户发送请求,servlet收到请求,servlet处理请求(业务逻辑分派给具体的处理器处理),servlet返回处理结果。


问题1:

用户的的请求是如何给到Spring MVC的前端控制器(DispatcherServlet)的?

答案在web.xml配置文件中:


在使用Spring MVC时,我们需要在web.xml中配置一个serlvet,这个servlet就是前端控制器DispatcherServlet。


load-on-startup

:表示启动容器时初始化该Servlet;


url-pattern

:表示哪些请求交给DispatcherServlet(Spring MVC)处理, “/” 是用来定义默认servlet映射的。

进行这样的配置后,web容器就将所有符合要求的请求交给了DispatcherServlet也就是Spring MVC来处理。


问题2:

DispatcherServlet是如何具体处理接收到的请求的?

DispatcherServlet核心处理流程:


DispatcherServlet核心代码(spring-webmvc-4.1.4):

当DispatcherServlet接收到请求后首先进入doService方法,然后调用核心分派方法doDispatch

doService代码:

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 1. 保存现场,以便能在必要时恢复。
        // 2. 将框架需要的对象放入request中,以便view和handler使用。
        try {
            // 3. 请求分派.
            doDispatch(request, response);
        } finally {
            // 4. 恢复现场。
        }
    }

接下来着重看下doDispatch方法的代码:

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		// 获取WebAsyncManager
		// WebAsyncManager是 异步处理的入口类 同时对Callables和DeferredResults启动的管理
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 检查请求是否是multipart(如文件上传)
				processedRequest = checkMultipart(request);
				// 如果是multipart那么MultipartResolver会根据request重新构建一个请求,此处判断是否为原请求
				multipartRequestParsed = (processedRequest != request);

				// 通过HandlerMapping匹配某个具体的页面控制器/处理器
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 使用适配器,将匹配到的的处理器包装成相应的适配器,以支持更多种类型的处理器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// last-modified 缓存支持
				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;
					}
				}

				// 执行处理器相关的拦截器的预处理
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// 适配器调用处理器的功能处理方法,返回一个ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(request, mv);
				// 执行处理器相关的拦截器的后置处理
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			} catch (Exception ex) {
				dispatchException = ex;
			}
			// 由ViewResolver解析渲染View,渲染时会把Model传入。
			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);
				}
			}
		}
	}

解析渲染view代码:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		// 省略代码
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		// 省略代码
	}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 省略代码

		// 获取视图(View)
		View view;
		if (mv.isReference()) {
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName()
						+ "' in servlet with name '" + getServletName() + "'");
			}
		} else {
			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() + "'");
			}
		}
		// 省略代码
		try {
			// 使用Model渲染视图
			view.render(mv.getModelInternal(), request, response);
		} catch (Exception ex) {
			// 省略代码
		}
	}

1、  收到请求。首先用户发送请求给DispatcherServlet(前端控制器),前端控制器收到请求后自己不进行处理,而是委托给其他的处理器进行处理,作为统一访问点,进行全局的流程控制;

2、  得到处理器。DispatcherServlet接收到请求,根据HandlerMapping将请求映射为HandlerExecutionChain对象(包含一个Handler处理器、多个HandlerInterceptor拦截器);

3、  处理器处理请求。HandlerAdapter将HandlerExecutionChain对象中的Handler处理器包装为适配器,然后通过适配器调用处理器功能处理方法真正的处理请求,并返回一个ModelAndView对象(包含模型数据和视图/视图名);

5、  得到视图。如果ModelAndView对象中包含的是逻辑视图名则调用ViewResolver将把逻辑视图名解析为具体的View,如果ModelAndView对象中包含的已经是一个View则直接返回该View;

6、  渲染视图。View根据Model模型数据进行渲染,此处的Model实际是一个Map数据结构;

7、  返回结果。DispatcherServlet返回响应给用户。



Spring MVC 主要组件

经过上面的流程分析,Spring MVC的主要组件也就比较清晰了:

1、前端控制器 DispatcherServlet

作用:统一访问点,进行全局的流程控制,有了DispatcherServlet 就减少了其它组件之间的耦合度

2、处理器映射器HandlerMapping

作用:根据请求的URL来查找Handler

3、处理器Handler(

需要程序员开发



作用:具体处理用户请求

4、处理器适配器HandlerAdapter

作用:按照特定的规则(HandlerAdapter要求的规则)去执行Handler。适配器设计模式的应用,从而很容易支持很多类型的处理器。

5、视图解析器 ViewResolver

作用:进行视图的解析 根据逻辑视图名解析成真正的视图(View)

6、视图View(

需要程序员开发


View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,velocity……)



Spring MVC的优点






1.清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器(Validator)、命令对象(Command Object)、表单对象(Form Object)。每一个角色都可以由一个专门的对象来实现。

2.强大而直接的配置方式:将框架类和应用程序累都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。

3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。

4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。

5.可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。

6.可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。

7.灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。

8.可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity、支持Freemarker(不需要额外的中间层)等等。

9.简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。

10.JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。



版权声明:本文为yangshen_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。