最近開始學習Spring MVC框架,在傳智博客的課程資源中找到了Spring MVC的教學視頻,本文也作為課程筆記與小結。
Spring MVC
Spring MVC屬于SpringFrameWork的后續產品,已經融合在Spring Web Flow里面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。
在開始入門程序之前,先了解下Spring MVC的工作原理。
Spring MVC工作流程
Spring MVC的工作流程可用下面這張圖表示:
Spring MVC主要由前端控制器 DispatcherServlet、處理器映射器 HandlerMapping、處理器適配器 HandlerAdapter、處理器 Handler、視圖解析器 ViewResolver 以及 視圖 View 組成。
從命名上看,這里的DispatcherServlet就是一個Servlet,所有請求正是從這個Servlet開始,而Spring MVC正是采用了前端控制器模式(Front Controller Pattern)。所謂前端控制器模式,是用來提供一個集中的請求處理機制,所有的請求都將由一個單一的處理程序處理。該處理程序可以做認證/授權/記錄日志,或者跟蹤請求,然后把請求傳給相應的處理程序。
對照上圖中的數字標號,Spring MVC的工作流程大致如下:
- 用戶發送請求至DispatcherServlet
- DispatcherServlet請求HandlerMapping查找Handler
- HandlerMapping向DispatcherServlet返回Handler
- DispatcherServlet請求HandlerAdapter執行Handler
- HandlerAdapter執行Handler
- Handler返回一個ModelAndView對象給HandlerAdapter,ModelAndView中包含模型數據和邏輯視圖名
- HandlerAdapter返回ModelAndView給DispatcherServlet
- DispatcherServlet請求ViewResolver將邏輯視圖名解析為具體的View
- ViewResolver將解析出的具體的View返回給DispatcherServlet
- View根據傳過來的Model數據進行渲染,Model實際上是一個Map
- 視圖渲染完畢后最終由DispatcherServlet響應給用戶
在實際開發中,以上幾個模塊Handler和View需要我們編寫,其它模塊均需要進行配置,后面的入門程序中會講到。
Spring MVC 入門程序
首先介紹我所使用的開發環境
- JDK 1.6.0_23
- Tomcat 7.0.69
- Spring Framework 4.3.3
- IDE: IntelliJ IDEA 15.0.6
入門程序是實現一個簡單的商品查詢功能,相關數據將采用靜態數據的方式。
1.建立工程,搭建開發環境
項目目錄如上所示,其中config文件夾作為存放一些xml配置文件的目錄,建立文件夾后需要右鍵Make Directory As Sources Root才能作為classpath使用。web/WEB-INF/lib文件夾用于存放項目所用到的jar包,將Spring和其它需要的jar包放入。
其中
spring-webmvc-4.3.3.RELEASE.jar
便是Spring MVC模塊,另外mybatis是為后續開發準備,入門項目暫時用不到。其它項目相關配置(如Tomcat)在這不多提及。
2.配置DispatcherServlet
前面提及DispatcherServlet實際就是一個Servlet,所以應在web.xml中配置這個Servlet。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>DispatchServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatchServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
參數contentConfigLocation
用于指定Spring配置文件的位置,這里是項目目錄中的config下的spring-mvc.xml
。
3.配置其它模塊/編寫Handler
接下來,需要對HandlerMapping、HandlerAdapter、ViewResolver進行配置,首先介紹使用非注解方式的配置。
3.1使用非注解方式
在Spring MVC中,由多種HandlerMapping和HandlerAdapter可供我們使用,首先介紹HandlerAdapter的配置。
第一種:SimpleControllerHandlerAdapter
使用SimpleControllerHandlerAdapter要求所編寫的Handler實現Controller接口。
在Spring配置文件spring-mvc.xml添加相應的bean:
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
Handler類:
public class ItemsControllerA implements Controller
{
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception
{
List<Items> itemsList = new ArrayList<Items>();
Items items1 = new Items();
items1.setName("Microsoft Surface Pro 4");
items1.setPrice(8900f);
items1.setDetail("Surface");
Items items2 = new Items();
items2.setName("Microsoft Surface Studio");
items2.setPrice(20000f);
items2.setDetail("Surface");
itemsList.add(items1);
itemsList.add(items2);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
}
Items
是一個po類,擁有一些基本屬性和相應的getter和setter方法(如id、name、price等),這里作為入門示例使用了靜態數據,itemsList.jsp
位于/WEB-INF/jsp/items
下,里面僅有一個表格用于數據的展示。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form>
商品列表:
<table>
<tr>
<td>商品名稱</td>
<td>商品價格</td>
<td>商品描述</td>
</tr>
<c:forEach items="${itemsList}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.price}</td>
<td>${item.detail}</td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
接下來還需要對HandlerMapping配置,首先介紹BeanNameUrlHandlerMapping:使用該映射器要求在配置Handler類時指定bean的name屬性,且name作為url,配置如下:
<!-- Handler -->
<bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>
<!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
最后還有ViewResolver的配置,對于jsp頁面,使用InternalResourceViewResolver
。
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
spring-mvc.xml完整內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<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:content="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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<!-- Handler -->
<bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>
<!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- SimpleControllerHandlerAdapter: 要求Handler實現Controller接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
至此,相關的程序編碼和配置均已完成,啟動Tomcat,將項目部署至Tomcat中,訪問鏈接
另外一種映射器叫做SimpleUrlHandlerMapping,使用方式如下:
<!-- SimpleUrlHandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- key為url,值為bean id -->
<prop key="/queryItems1.action">ItemsControllerA</prop>
</props>
</property>
</bean>
第二種 HttpRequestHandlerAdapter
使用HttpRequestHandlerAdapter要求Handler實現HttpRequestHandler接口,使用如下(直接在spring-mvc.xml繼續添加了bean):
Handler:
public class ItemsControllerB implements HttpRequestHandler
{
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
{
List<Items> itemsList = new ArrayList<Items>();
Items items1 = new Items();
items1.setName("Microsoft Surface Pro 4");
items1.setPrice(8900f);
items1.setDetail("Surface");
Items items2 = new Items();
items2.setName("Microsoft Surface Studio");
items2.setPrice(20000f);
items2.setDetail("Surface");
itemsList.add(items1);
itemsList.add(items2);
httpServletRequest.setAttribute("itemsList", itemsList);
httpServletRequest.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(httpServletRequest, httpServletResponse);
}
}
spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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:content="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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<!-- Handler -->
<bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>
<bean id="ItemsControllerB" class="com.dstudiow.ssm.controller.ItemsControllerB"/>
<!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<!-- SimpleUrlHandlerMapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- key為url,值為bean id -->
<prop key="/queryItems1.action">ItemsControllerA</prop>
<prop key="/queryItems2.action">ItemsControllerB</prop>
</props>
</property>
</bean>
<!-- SimpleControllerHandlerAdapter: 要求Handler實現Controller接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- HttpRequestHandlerAdapter: 要求Handler實現HttpRequestHandler接口 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
使用HttpRequestHandlerAdapter時Handler中handleRequest()方法與Servlet中的doGet()或doPost()方法幾乎一樣,使用這種方法可以根據需求設置相應數據格式(如不需要返回頁面而是Json數據,使用httpServletResponse.getWriter().write()
寫入Json字符串)。
以上便是使用非注解配置方式的HandlerAdapter和HandlerMapping的內容,通過學習也了解到了非注解方式存在明顯的缺點,一個Handler只有一個方法實現我們的功能,在需求較多的情況下,如本案例中同樣是商品的相關請求,需要獲取全部商品/根據id查找商品/修改商品信息等都需要寫不同的Handler,比較麻煩,使用注解的方式正能解決這一問題。
3.2 使用非注解方式
首先需要在Spring配置文件中配置<content:component-scan>
,開啟Spring的組件掃描。
<content:component-scan base-package="com.dstudiow.ssm.controller"/>
接下來是注解方式所使用的處理器適配器以及處理器映射器,分別為RequestMappingHandlerAdapter
和RequestMappingHandlerMapping
,在配置文件加入bean即可:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
除此之外,可以使用<mvc:annotation-driven/>
標簽,該標簽幫我們默認注冊了RequestMappingHandlerAdapter
和RequestMappingHandlerMapping
這兩個bean。而在Spring 3.2之前的版本中默認注冊的為DefaultAnnotationHandlerMapping
與AnnotationMethodHandlerAdapter
,這兩個類目前已被廢棄。
接下來是Handler類:
@Controller
public class ItemsControllerC
{
@RequestMapping("/queryItems3.action")
public ModelAndView queryItems() throws Exception
{
List<Items> itemsList = new ArrayList<Items>();
Items items1 = new Items();
items1.setName("Microsoft Surface Pro 4");
items1.setPrice(8900f);
items1.setDetail("Surface");
Items items2 = new Items();
items2.setName("Microsoft Surface Studio");
items2.setPrice(20000f);
items2.setDetail("Surface");
itemsList.add(items1);
itemsList.add(items2);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
}
@Controller
注解標識此類為一個Handler,@RequestMapping
標識所對應的訪問路徑。
ViewResolver
在前面的示例中,Handler會返回一個ModelAndView對象,通過setViewName()方法設置了對應的視圖絕對路徑,根據我們項目目錄的劃分,這個完整路徑一般還是比較長的,每次都在代碼中寫下完整的路徑未免有些繁瑣,所以ViewResolver有關于url前后綴的屬性可以配置,如下:
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
Handler就不需要寫下完整路徑了:
modelAndView.setViewName("items/itemsList");