SPRingMVC是一個基于MVC的Web框架,是spring框架的一個模塊,使用了MVC架構模式的思想,將web層進行職責解耦。首先讓我們整體看一下SpringMVC處理請求的流程:

【源碼分析】
首先是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.(根據(jù)處理器得到相應的適配器) 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; } } //執(zhí)行預處理 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler.(上圖中步驟4:請求處理器適配器HandlerAdapter執(zhí)行Handler) mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // 執(zhí)行相應的后處理 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);}【優(yōu)點】
1、清晰的角色劃分:前端控制器(DispatcherServlet)、請求到處理器映射(HandlerMapping)、處理器適配器(HandlerAdapter)、視圖解析器(ViewResolver)、處理器或頁面控制器(Controller)、驗證器( Validator)、命令對象(Command 請求參數(shù)綁定到的對象就叫命令對象)、表單對象(Form Object提供給表單展示和提交到的對象就叫表單對象)。
2、和Spring其他框架無縫集成,是其它Web框架所不具備的;
3、可適配,通過HandlerAdapter可以支持任意的類作為處理器;
4、可定制性,HandlerMapping、ViewResolver等能夠非常簡單的定制;
5、功能強大的數(shù)據(jù)驗證、格式化、綁定機制;
6、強大的JSP標簽庫,使JSP編寫更容易。
【缺點】
1.SpringMVC與servlet API耦合,難以脫離servlet容器獨立運行,降低了SpringMVC框架的可擴展性。
2.太過細化的角色劃分,太過繁瑣,降低了應用的開發(fā)效率。
【入門小程序】
開發(fā)環(huán)境: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進行解析,對于靜態(tài)文件的解析需要配置不讓DispatcherServlet進行解析 使用此種方式可以實現(xiàn) RESTful風格的url 第三種:/*,這樣配置不對,使用這種配置,最終要轉發(fā)到一個jsp頁面時, 仍然會由DispatcherServlet解析jsp地址,不能根據(jù)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" /> <!-- **************************************************************************************** --> <!-- **************************************************************************************** --> <!-- 處理器適配器 --> <!-- 所有的處理器適配器都實現(xiàn)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;/** * 實現(xiàn)controller接口的處理器 * * @author happy * */public class ItemsController1 implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // 調用Services查找數(shù)據(jù)庫,查詢商品列表,這里使用靜態(tài)數(shù)據(jù)模擬 List<Items> itemsList = new ArrayList<Items>(); // 想List中填充靜態(tài)數(shù)據(jù) Items items_1 = new Items(); items_1.setName("聯(lián)想筆記本"); items_1.setPrice(6000f); items_1.setDetail("ThinkPad T430 聯(lián)想筆記本電腦!"); 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取數(shù)據(jù) 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>
新聞熱點
疑難解答