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.(根据处理器得到相应的适配器) 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>
新闻热点
疑难解答