首页 > 学院 > 开发设计 > 正文

【SpringMVC】SpringMVC初学详解篇

2019-11-06 08:48:59
字体:
来源:转载
供稿:网友

      SPRingMVC是一个基于MVC的Web框架,是spring框架的一个模块,使用了MVC架构模式的思想,将web层进行职责解耦。首先让我们整体看一下SpringMVC处理请求的流程:

发起请求到前端控制器(DispatcherServlet)前端控制器请求HandlerMapping查找Handler,可以根据xml配置、注解进行查找处理器映射器HandlerMapping向前端控制器返回Handler前端控制器调用处理器适配器去执行Handler处理器适配器去执行HandlerHandler执行完成给适配器返回ModelAndView处理器适配器向前端控制器返回ModelAndView(springmvc框架的一个底层对象,包括Model和view)前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)视图解析器向前端控制器返回View前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域前端控制器向用户响应结果

源码分析】

      首先是SpringMVC的入口类DispatcherServlet,该类其实是一个servlet类,(可以从web.xml文件的配置中直接点击进去查看类源码),前端控制器接收到请求之后,会调用它的doService方法,然后调用doDispatch方法,重点就是doDispatch方法,源码如下:

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);			// 如果请求方式为multipart,则通过multipart进行解析			multipartRequestParsed = processedRequest != request;			// Determine handler for the current request.(上图中的步骤2:通过调用getHandler方法,调用处理器映射器HandlerMapping查找Handler,详见方法二)			mappedHandler = getHandler(processedRequest, false);			if (mappedHandler == null || mappedHandler.getHandler() == 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 (logger.isDebugEnabled()) {					String requestUri = urlPathHelper.getRequestUri(request);					logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);				}				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {					return;				}			}                                //执行预处理			if (!mappedHandler.applyPreHandle(processedRequest, response)) {				return;			}			try {				// Actually invoke the handler.(上图中步骤4:请求处理器适配器HandlerAdapter执行Handler)				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());			}			finally {				if (asyncManager.isConcurrentHandlingStarted()) {					return;				}			}			applyDefaultViewName(request, mv);			// 执行相应的后处理			mappedHandler.applyPostHandle(processedRequest, response, mv);		}		catch (Exception ex) {			dispatchException = ex;		}		//该方法详情见方法三(上图中的步骤8、9、10)		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			mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);			return;		}		// Clean up any resources used by a multipart request.		if (multipartRequestParsed) {			cleanupMultipart(processedRequest);		}	}}

②方法二(getHandler方法源码)

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

③方法三(processDispatchResult方法源码)

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?(上图中的步骤8:通过render方法,请求视图解析器view Resolver解析视图,具体源码如方法四)		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);		}	}

④方法四(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 = 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 {		// 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() + "'");	}	view.render(mv.getModelInternal(), request, response);}

【优点】

      1、清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器(   Validator)、命令对象(Command  请求参数绑定到的对象就叫命令对象)、表单对象(Form Object提供给表单展示和提交到的对象就叫表单对象)。

      2、和Spring其他框架无缝集成,是其它Web框架所不具备的;

      3、可适配,通过HandlerAdapter可以支持任意的类作为处理器;

      4、可定制性,HandlerMapping、ViewResolver等能够非常简单的定制;

      5、功能强大的数据验证、格式化、绑定机制;

      6、强大的JSP标签库,使JSP编写更容易。

【缺点】

      1.SpringMVC与servlet API耦合,难以脱离servlet容器独立运行,降低了SpringMVC框架的可扩展性。

      2.太过细化的角色划分,太过繁琐,降低了应用的开发效率。

【入门小程序】

开发环境:eclipse、MySQL、JDK、tomcat、引入如下图所示jar包(点击链接下载jar包)

程序结构如下:

①Items类:

package cn.itcast.ssm.po;import java.util.Date;public class Items {	private Integer id;	private String name;	private Float price;	private String pic;	private Date createtime;	private String detail;	// get和set方法略	}

②Web.xml代码:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"	xmlns="http://java.sun.com/xml/ns/javaee"	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"	id="WebApp_ID" version="3.0">	<display-name>mySpringMVC</display-name>	<!-- springmvc前端控制器 -->	<servlet>		<servlet-name>springmvc</servlet-name>		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>		<!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等)如果不配置,默认加载的是WEB-INF/servlet名称-servlet.xml -->		<init-param>			<param-name>contextConfigLocation</param-name>			<param-value>classpath:springmvc.xml</param-value>		</init-param>	</servlet>	<!-- 第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析 第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析 		使用此种方式可以实现 RESTful风格的url 第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时, 仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错。 -->	<servlet-mapping>		<servlet-name>springmvc</servlet-name>		<url-pattern>*.action</url-pattern>	</servlet-mapping>	<welcome-file-list>		<welcome-file>index.html</welcome-file>		<welcome-file>index.htm</welcome-file>		<welcome-file>index.jsp</welcome-file>		<welcome-file>default.html</welcome-file>		<welcome-file>default.htm</welcome-file>		<welcome-file>default.jsp</welcome-file>	</welcome-file-list></web-app>

③Springmvc.xml代码:

<beans xmlns="http://www.springframework.org/schema/beans"	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"	xmlns:context="http://www.springframework.org/schema/context"	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"	xsi:schemaLocation="http://www.springframework.org/schema/beans 		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 		http://www.springframework.org/schema/mvc 		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 		http://www.springframework.org/schema/context 		http://www.springframework.org/schema/context/spring-context-3.2.xsd 		http://www.springframework.org/schema/aop 		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 		http://www.springframework.org/schema/tx 		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">	<!-- **************************************************************************************** -->	<!-- 配置Handler -->	<bean name="/queryItems.action" class="cn.itcast.ssm.controller.ItemsController1" />	<!-- **************************************************************************************** -->	<!-- **************************************************************************************** -->	<!-- 处理器映射器 -->	<!-- 将bean的name作为 URL进行查询,需要在配置Handler时指定beanname(就是url) -->	<bean		class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />	<!-- **************************************************************************************** -->	<!-- **************************************************************************************** -->	<!-- 处理器适配器 -->	<!-- 所有的处理器适配器都实现HandlerAdapter接口,该接口中有boolean supports(Object handler); 		方法,通过这个supports方法来判断寻找相应的Handler -->	<bean		class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />	<!-- **************************************************************************************** -->	<!-- **************************************************************************************** -->	<!-- 视图解析器 -->	<!--解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包 -->	<bean		class="org.springframework.web.servlet.view.InternalResourceViewResolver">	</bean>	<!-- **************************************************************************************** --></beans>

④controller代码:

package cn.itcast.ssm.controller;import java.util.ArrayList;import java.util.List;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import cn.itcast.ssm.po.Items;/** * 实现controller接口的处理器 *  * @author happy * */public class ItemsController1 implements Controller {	@Override	public ModelAndView handleRequest(HttpServletRequest request,			HttpServletResponse response) throws Exception {		// 调用Services查找数据库,查询商品列表,这里使用静态数据模拟		List<Items> itemsList = new ArrayList<Items>();		// 想List中填充静态数据		Items items_1 = new Items();		items_1.setName("联想笔记本");		items_1.setPrice(6000f);		items_1.setDetail("ThinkPad T430 联想笔记本电脑!");		Items items_2 = new Items();		items_2.setName("苹果手机");		items_2.setPrice(5000f);		items_2.setDetail("iphone6苹果手机!");		itemsList.add(items_1);		itemsList.add(items_2);		// 返回ModelAndView		ModelAndView modelAndView = new ModelAndView();		// 相当于request的setAttribute,在jsp页面中通过itemsList取数据		modelAndView.addObject("itemsList", itemsList);		// 指定视图		modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");		return modelAndView;	}}

⑤itemsList.jsp代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>查询商品列表</title></head><body> <form action="${pageContext.request.contextPath }/item/queryItem.action" method="post">查询条件:<table width="100%" border=1><tr><td><input type="submit" value="查询"/></td></tr></table>商品列表:<table width="100%" border=1><tr>	<td>商品名称</td>	<td>商品价格</td>	<td>生产日期</td>	<td>商品描述</td>	<td>操作</td></tr><c:forEach items="${itemsList }" var="item"><tr>	<td>${item.name }</td>	<td>${item.price }</td>	<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>	<td>${item.detail }</td>		<td><a href="${pageContext.request.contextPath }/item/editItem.action?id=${item.id}">修改</a></td></tr></c:forEach></table></form></body></html>


上一篇:重在坚持

下一篇:单源最短路径

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表