1.Spring web mvc介紹
Spring web mvc和Struts2都屬于表現(xiàn)層的框架,它是Spring框架的一部分,我們可以從Spring的整體結(jié)構(gòu)中看得出來(lái):
2.Web mvc
用戶發(fā)起request請(qǐng)求至控制器(Controller)
控制接收用戶請(qǐng)求的數(shù)據(jù),委托給模型進(jìn)行處理控制器通過(guò)模型(Model)處理數(shù)據(jù)并得到處理結(jié)果
模型通常是指業(yè)務(wù)邏輯控制器將模型數(shù)據(jù)在視圖(View)中展示
web中模型無(wú)法將數(shù)據(jù)直接在視圖上顯示,需要通過(guò)控制器完成。如果在C/S應(yīng)用中模型是可以將數(shù)據(jù)在視圖中展示的脓斩。控制器將視圖response響應(yīng)給用戶
通過(guò)視圖展示給用戶要的數(shù)據(jù)或處理結(jié)果耗拓。
3.Spring web mvc 架構(gòu)
架構(gòu)圖
流程
- 用戶發(fā)送請(qǐng)求至前端控制器
DispatcherServlet
. -
DispatcherServlet
收到請(qǐng)求調(diào)用HandlerMapping
處理器映射器. - 處理器映射器找到具體的處理器揣苏,生成處理器對(duì)象及處理器攔截器(如果有則生成)一并返回給
DispatcherServlet
. -
DispatcherServlet
調(diào)用HandlerAdapter
處理器適配器. -
HandlerAdapter
經(jīng)過(guò)適配調(diào)用具體的處理器(Controller涮因,也叫后端控制器). -
Controller
執(zhí)行完成返回ModelAndView
. -
HandlerAdapter
將controller
執(zhí)行結(jié)果ModelAndView
返回給DispatcherServlet
. -
DispatcherServlet
將ModelAndView
傳給ViewReslover
視圖解析器. -
ViewReslover
解析后返回具體View
. -
DispatcherServlet
根據(jù)View進(jìn)行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)。 -
DispatcherServlet
響應(yīng)用戶
組件說(shuō)明:
++以下組件通常使用框架提供實(shí)現(xiàn):++
DispatcherServlet
:作為前端控制器施符,整個(gè)流程控制的中心,控制其它組件執(zhí)行擂找,統(tǒng)一調(diào)度戳吝,降低組件之間的耦合性,提高每個(gè)組件的擴(kuò)展性贯涎。
HandlerMapping
:通過(guò)擴(kuò)展處理器映射器實(shí)現(xiàn)不同的映射方式听哭,例如:配置文件方式,實(shí)現(xiàn)接口方式塘雳,注解方式等陆盘。
HandlAdapter
:通過(guò)擴(kuò)展處理器適配器,支持更多類型的處理器粉捻。
ViewResolver
:通過(guò)擴(kuò)展視圖解析器礁遣,支持更多類型的視圖解析,例如:jsp肩刃、freemarker祟霍、pdf、excel等盈包。
++下邊兩個(gè)組件通常情況下需要開(kāi)發(fā)++:
Handler
:處理器沸呐,即后端控制器用controller表示。
View
:視圖呢燥,即展示給用戶的界面崭添,視圖中通常需要標(biāo)簽語(yǔ)言展示模型數(shù)據(jù)。
4.開(kāi)發(fā)環(huán)境準(zhǔn)備
本教程使用Eclipse+tomcat7開(kāi)發(fā)
詳細(xì)參考“Eclipse開(kāi)發(fā)環(huán)境配置-indigo.docx”文檔
5.第一個(gè)springmvc工程
第一步:建立一個(gè)Web項(xiàng)目
在eclipse下創(chuàng)建動(dòng)態(tài)web工程springmvc_01叛氨。
第二步:導(dǎo)入spring3.1.4的jar包
第三步:前端控制器配置
在WEB-INF\web.xml中配置前端控制器呼渣,
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
// 表示servlet隨服務(wù)啟動(dòng)
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
// 以.action結(jié)尾的請(qǐng)求交給DispatcherServlet處理
<url-pattern>*.action</url-pattern>
</servlet-mapping>
load-on-startup
:表示servlet隨服務(wù)啟動(dòng);
url-pattern
:*.action的請(qǐng)交給DispatcherServlet處理寞埠。
contextConfigLocation
:指定springmvc配置的加載位置屁置,如果不指定則默認(rèn)加載WEB-INF/[DispatcherServlet 的Servlet 名字]-servlet.xml。
第四步:springmvc配置文件
Springmvc默認(rèn)加載WEB-INF/[前端控制器的名字]-servlet.xml仁连,也可以在前端控制器定義處指定加載的配置文件蓝角,如下:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
如上代碼,通過(guò)contextConfigLocation
加載classpath下的springmvc-servlet.xml
配置文件,配置文件名稱可以不限定[前端控制器的名字]-servlet.xml使鹅。
第五步:配置處理器映射器
在springmvc-servlet.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.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">
<!-- 處理器映射器 -->
<!-- 根據(jù)bean的name進(jìn)行查找Handler 將action的url配置在bean的name中 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
BeanNameUrlHandlerMapping
:表示將定義的Bean名字作為請(qǐng)求的url揪阶,需要將編寫的controller在spring容器中進(jìn)行配置,且指定bean的name為請(qǐng)求的url患朱,且必須以.action結(jié)尾鲁僚。
第六步:配置處理器適配器
在springmvc-servlet.xml文件配置如下:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
SimpleControllerHandlerAdapter
:即簡(jiǎn)單控制器處理適配器,所有實(shí)現(xiàn)了org.springframework.web.servlet.mvc.Controller 接口的Bean作為Springmvc的后端控制器麦乞。
第七步:配置視圖解析器
在springmvc-servlet.xml文件配置如下:
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
InternalResourceViewResolver
:支持JSP視圖解析.
viewClass:JstlView
表示JSP模板頁(yè)面需要使用JSTL標(biāo)簽庫(kù)蕴茴,所以classpath必須包含jstl的相關(guān)jar包;
prefix
和suffix
:查找視圖頁(yè)面的前綴和后綴姐直,最終視圖的址為:前綴+邏輯視圖名+后綴倦淀,邏輯視圖名需要在controller中返回ModelAndView指定,比如邏輯視圖名為hello声畏,則最終返回的jsp視圖地址“WEB-INF/jsp/hello.jsp”
第八步:后端控制器開(kāi)發(fā)
后端控制器即controller
撞叽,也有稱為action
。
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.springframework.web.servlet.ModelAndView;
importorg.springframework.web.servlet.mvc.Controller;
public class HelloWorldController implements Controller {
@Override
Public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
ModelAndView mv = new ModelAndView();
// 添加模型數(shù)據(jù)
mv.addObject("message", "Hello World!");
// 設(shè)置邏輯視圖名,最終視圖地址=前綴+邏輯視圖名+后綴
mv.setViewName("hello");
return mv;
}
}
org.springframework.web.servlet.mvc.Controller
:處理器必須實(shí)現(xiàn)Controller 接口插龄。
ModelAndView
:包含了模型數(shù)據(jù)及邏輯視圖名
第九步:后端控制器配置
在springmvc-servlet.xml文件配置如下:
<bean name="/hello.action" class="springmvc.action.HelloWorldController"/>
name="/hello.action"
:前邊配置的BeanNameUrlHandlerMapping愿棋,表示如過(guò)請(qǐng)求的URL為“上下文/hello.action”,則將會(huì)交給該Bean進(jìn)行處理均牢。
第十步:視圖開(kāi)發(fā)
創(chuàng)建/WEB-INF/jsp/hello.jsp視圖頁(yè)面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>第一個(gè)程序</title>
</head>
<body>
<!-- 顯示一行信息hello world!!!! -->
${message}
</body>
</html>
${message}
:表示顯示由HelloWorldController處理器傳過(guò)來(lái)的模型數(shù)據(jù)糠雨。
第十一步:部署在tomcat測(cè)試
通過(guò)請(qǐng)求:http://localhost:8080/springmvc_01/hello.action,如果頁(yè)面輸出“Hello World! ”就表明我們成功了徘跪。
總結(jié):
主要進(jìn)行如下操作:
- 前端控制器DispatcherServlet配置
加載springmvc的配置文件 - HandlerMapping配置
- HandlerAdapter配置
- ViewResolver配置
前綴和后綴 - 后端控制器編寫
- 后端控制器配置
- 視圖編寫
從上邊的步驟可以看出甘邀,通常情況下我們只需要編寫后端控制器和視圖。
6.HandlerMapping處理器映射器
HandlerMapping 給前端控制器返回一個(gè)HandlerExecutionChain 對(duì)象(包含一個(gè)Handler (后端控制器)對(duì)象垮庐、多個(gè)HandlerInterceptor 攔截器)對(duì)象松邪。
BeanNameUrlHandlerMapping
beanName Url映射器
<!—beanName Url映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
將后端控制器的bean name作為請(qǐng)求的url。
SimpleUrlHandlerMapping
<!—簡(jiǎn)單url映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello1.action">hello_controller</prop>
<prop key="/hello2.action">hello_controller</prop>
</props>
</property>
</bean>
可定義多個(gè)url映射至一個(gè)后端控制器哨查,hello_controller為后端控制器bean的id逗抑。
7.HandlerAdapter處理器適配器
HandlerAdapter會(huì)把后端控制器包裝為一個(gè)適配器,支持多種類型的控制器開(kāi)發(fā)寒亥,這里使用了適配器設(shè)計(jì)模式邮府。
SimpleControllerHandlerAdapter
簡(jiǎn)單控制器處理器適配器.
所有實(shí)現(xiàn)了org.springframework.web.servlet.mvc.Controller
接口的Bean作為Springmvc的后端控制器。
適配器配置如下:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
HttpRequestHandlerAdapter
HTTP請(qǐng)求處理器適配器.
HTTP請(qǐng)求處理器適配器將http請(qǐng)求封裝成HttpServletResquest
和HttpServletResponse
對(duì)象溉奕,和servlet
接口類似褂傀。
適配器配置如下:
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
Controller實(shí)現(xiàn)如下:
publicclass HelloWorldController2 implements HttpRequestHandler {
@Override
publicvoid handleRequest(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("message", "HelloWorld!");
request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request, response);
// 也可以自定義響應(yīng)內(nèi)容
//response.setCharacterEncoding("utf-8");
//response.getWriter().write("HelloWorld!");
}
}
從上邊可以看出此適配器器的controller方法沒(méi)有返回ModelAndView
,可通過(guò)response
修改定義響應(yīng)內(nèi)容腐宋。
8.Controller控制器
AbstractCommandController(命令控制器)
該控制器能把請(qǐng)求參數(shù)封裝到一個(gè)命令對(duì)象(模型對(duì)象)中。
public class MyCommandController extends AbstractCommandController {
/**
* 通過(guò)構(gòu)造函數(shù)設(shè)置命令對(duì)象
*/
public MyCommandController(){
this.setCommandClass(Student.class);
this.setCommandName("student");//不是必須設(shè)置
}
@Override
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
Student student = (Student)command;
System.out.println(student);
Return null;
}
/**
* 字符串轉(zhuǎn)換為日期屬性編輯器
*/
@Override
Protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
}
publicclass Student {
public Student() {
}
public Student(String name) {
this.name = name;
}
private String name;//姓名
private Integer age;//年齡
private Date birthday;//生日
private String address;//地址
….get/set方法省略
Controller配置;
<bean id="command_controller" name="/command.action" class="springmvc.action.MyCommandController"/>
使用命令控制器完成查詢列表及表單提交
代碼略
9.問(wèn)題解決
日期格式化
在controller注冊(cè)屬性編輯器:
/**
* 注冊(cè)屬性編輯器(字符串轉(zhuǎn)換為日期)
*/
@InitBinder
public void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
Post時(shí)中文亂碼
在web.xml
中加入:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
以上可以解決post請(qǐng)求亂碼問(wèn)題。
對(duì)于get請(qǐng)求中文參數(shù)出現(xiàn)亂碼解決方法有兩個(gè):
修改tomcat配置文件添加編碼與工程編碼一致胸竞,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一種方法對(duì)參數(shù)進(jìn)行重新編碼:
String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默認(rèn)編碼欺嗤,需要將tomcat編碼后的內(nèi)容按utf-8編碼
10.注解開(kāi)發(fā)
第一個(gè)例子
創(chuàng)建工程的步驟同第一個(gè)springmvc工程,注解開(kāi)發(fā)需要修改handlermapper
和handlMapperAdapter
卫枝,如下:
在springmvc-servlet.xml
中配置:
<!--注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
注解映射器和注解適配器可以使用 <mvc:annotation-driven />
代替煎饼。
<mvc:annotation-driven />
默認(rèn)注冊(cè)了注解映射器和注解適配器等bean。
HelloWorldController編寫:
@Controller
publicclass HelloWorldController {
@RequestMapping(value="/hello")
public String hello(Model model)throws Exception{
model.addAttribute("message", "HelloWorld!");
return"hello";
}
}
注解描述:
@Controller
:用于標(biāo)識(shí)是處理器類
@RequestMapping
:請(qǐng)求到處理器功能方法的映射規(guī)則
Controller配置
在springmvc-servlet.xml中配置定義的controller:
<!-- controller -->
<bean class="springmvc.action.HelloWorldController"/>
組件掃描
<context:component-scan base-package="springmvc.action" />
掃描 @component
校赤、@controller
吆玖、@service
、@repository
的注解
注意:如果使用組件掃描則controller
不需要在springmvc-servlet.xml
中配置
11.與struts2不同
- springmvc的入口是一個(gè)servlet即前端控制器马篮,而struts2入口是一個(gè)filter過(guò)慮器沾乘。
- springmvc是基于方法開(kāi)發(fā),請(qǐng)求參數(shù)傳遞到方法的形參浑测,可以設(shè)計(jì)為單例或多例(建議單例)翅阵,struts2是基于類開(kāi)發(fā),傳遞參數(shù)是通過(guò)類的屬性迁央,只能設(shè)計(jì)為多例掷匠。
- Struts采用值棧存儲(chǔ)請(qǐng)求和響應(yīng)的數(shù)據(jù),通過(guò)OGNL存取數(shù)據(jù)岖圈, springmvc通過(guò)參數(shù)解析器是將request請(qǐng)求內(nèi)容解析讹语,并給方法形參賦值,將數(shù)據(jù)和視圖封裝成ModelAndView對(duì)象蜂科,最后又將ModelAndView中的模型數(shù)據(jù)通過(guò)reques域傳輸?shù)巾?yè)面顽决。Jsp視圖解析器默認(rèn)使用jstl。
注意:
不要在action類中定義方法所用到的變量崇摄,變量要定義到方法體中擎值。因?yàn)榉椒ㄊ窃跅?nèi)存中,不會(huì)導(dǎo)致線程問(wèn)題逐抑。
12.@Controller
標(biāo)識(shí)該類為控制器類鸠儿,@controller
、@service
厕氨、@repository
分別對(duì)應(yīng)了web應(yīng)用三層架構(gòu)的組件即控制器进每、服務(wù)接口、數(shù)據(jù)訪問(wèn)接口命斧。
13.@RequestMapping
URL路徑映射
@RequestMapping(value="/user")或@RequestMapping("/user")
根路徑+子路徑
根路徑:
@RequestMapping
放在類名上邊田晚,如下:
@Controller
@RequestMapping("/user")
子路徑:
@RequestMapping
放在方法名上邊,如下:
@RequestMapping("/useradd")
public String useradd(….
URI 模板模式映射
@RequestMapping(value="/useredit/{userId}"):{×××}占位符国葬,請(qǐng)求的URL可以是“/useredit/001”或“/useredit/abc”贤徒,通過(guò)在方法中使用@PathVariable 獲取{×××}中的×××變量芹壕。
@RequestMapping("/useredit/{userid}")
public String useredit(@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable獲取useried的值,使用model傳回頁(yè)面
model.addAttribute("userid", userid);
return"/user/useredit";
}
實(shí)現(xiàn)restFul,所有的url都是一個(gè)資源的鏈接接奈,有利于搜索引擎對(duì)網(wǎng)址收錄踢涌。
多個(gè)占位符:
@RequestMapping("/useredit/{groupid}/{userid}")
public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable獲取useried的值,使用model傳回頁(yè)面
model.addAttribute("groupid", groupid);
model.addAttribute("userid", userid);
return"/user/useredit";
}
頁(yè)面:
<td>
<a href="${pageContext.request.contextPath}/user/editStudent/${student.id}/${student.groupId}/${student.sex}.action">修改</a>
</td>
請(qǐng)求方法限定
限定GET方法
@RequestMapping(method = RequestMethod.GET)
如果通過(guò)Post訪問(wèn)則報(bào)錯(cuò):
HTTP Status 405 - Request method 'POST' not supported
例如:
@RequestMapping(value="/useredit/{userid}",method=RequestMethod.GET)
限定POST方法
@RequestMapping(method = RequestMethod.POST)
如果通過(guò)Post訪問(wèn)則報(bào)錯(cuò):
HTTP Status 405 - Request method 'GET' not supported
GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
14.請(qǐng)求數(shù)據(jù)綁定(重點(diǎn))
Controller方法通過(guò)形參接收頁(yè)面?zhèn)鬟f的參數(shù)序宦。
如下:
@RequestMapping("/userlist")
public String userlist(
HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Model model
)
默認(rèn)支持的參數(shù)類型
HttpServletRequest
通過(guò)request對(duì)象獲取請(qǐng)求信息
HttpServletResponse
通過(guò)response處理響應(yīng)信息
HttpSession
通過(guò)session對(duì)象得到session中存放的對(duì)象
session.setAttribute("userinfo", student);
session.invalidate(); // 使session失效
Model
通過(guò)model向頁(yè)面?zhèn)鬟f數(shù)據(jù)睁壁,如下:
model.addAttribute("user", new User("李四"));
頁(yè)面通過(guò)${user.XXXX}獲取user對(duì)象的屬性值。
命令/表單對(duì)象
自動(dòng)將請(qǐng)求參數(shù)綁定到功能處理方法的命令/表單對(duì)象上互捌。
Controller方法通過(guò)形參接收命令/表單對(duì)象潘明。
Java基本數(shù)據(jù)類型
布爾型:
頁(yè)面如下:
<tr>
<td>用戶狀態(tài):</td>
<td>
<inputtype="radio"name="userstate"value="true"/>
<inputtype="radio"name="userstate"value="false"/>
</td>
</tr>
整型
例子略
單精度/雙精度
例子略
日期型需要添加屬性編輯器:
@InitBinder
Public void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
例子略
Pojo對(duì)象
頁(yè)面上以pojo對(duì)象中屬性名稱命名:
將pojo對(duì)象中的屬性名于傳遞進(jìn)來(lái)的屬性名對(duì)應(yīng),如果傳進(jìn)來(lái)的參數(shù)名稱和對(duì)象中的屬性名稱一致則將參數(shù)值設(shè)置在pojo對(duì)象中
頁(yè)面定義如下;
<input type="text" name="age"/>
<input type="text" name="birthday"/>
Contrller方法定義如下:
public String useraddsubmit(Model model,User user)throws Exception{
System.out.println(user);
}
頁(yè)面上以pojo對(duì)象名點(diǎn)屬性名命名:
如果采用類似struts中對(duì)象.屬性的方式命名秕噪,需要將pojo對(duì)象作為一個(gè)包裝對(duì)象的屬性钳降,action中以該包裝對(duì)象作為形參。
包裝對(duì)象定義如下:
Public class UserVo {
private User user;
public User getUser() {
return user;
}
Public void setUser(User user) {
this.user = user;
}
}
頁(yè)面定義:
<input type="text" name="user.age" />
<input type="text" name="user.birthday" />
Controller方法定義如下:
public String useraddsubmit(Model model,UserVo userVo)throws Exception{
System.out.println(userVo.getUser());
}
字符串?dāng)?shù)組
使用情景:頁(yè)面提交批量數(shù)據(jù)以數(shù)組接受
頁(yè)面定義如下:
頁(yè)面選中多個(gè)checkbox向controller方法傳遞
<c:forEach items="${studentList}" var="student">
<tr>
<td><input type="checkbox" name="deleteids" value="${student.id }"></td>
<td>${student.id}</td>
<td>${student.name}</td>
傳遞到controller方法中的格式是:001,002,003
Controller方法中可以用String[]接收巢价,定義如下:
@RequestMapping("/deleteStudent")
public String deleteStudent(String[] deleteids)throws Exception {
return "editStudentSubmit";
}
List
List中存放對(duì)象牲阁,并將定義的List放在包裝類中,action使用包裝對(duì)象接收壤躲。
List中對(duì)象:
成績(jī)對(duì)象
public class StudentScore {
private String coursename;//課程名稱
private Float score;//成績(jī)
public String getCoursename() {
returncoursename;
}
Public void setCoursename(String coursename) {
this.coursename = coursename;
}
public Float getScore() {
returnscore;
}
Public void setScore(Float score) {
this.score = score;
}
}
Public class UserVo {
Private List<StudentScore> scores;//成績(jī)
//get/set方法..
}
包裝類中定義List對(duì)象城菊,并添加get/set方法如下:
private List<StudentScore> scores;//成績(jī)
頁(yè)面:
<tr>
<td>課程成績(jī):</td>
<td>
課程名:<input type="text"name="scores[0].coursename"/>成績(jī):<input type="text"name="scores[0].score"/><br/>
課程名:<input type="text"name="scores[1].coursename"/>成績(jī):<input type="text"name="scores[1].score"/><br/>
課程名:<input type="text"name="scores[2].coursename"/>成績(jī):<input type="text"name="scores[2].score"/><br/>
</td>
</tr>
Contrller方法定義如下:
public String useraddsubmit(Model model,UserVo userVo)throws Exception{
System.out.println(userVo.getScores ());
}
Map
在包裝類中定義Map對(duì)象,并添加get/set方法碉克,action使用包裝對(duì)象接收凌唬。
包裝類中定義Map對(duì)象如下:
Public class UserVo {
private Map<String, Object>studentinfo = new HashMap<String, Object>();
//get/set方法..
}
頁(yè)面定義如下:
<tr>
<td>學(xué)生信息:</td>
<td>
姓名:<inputtype="text"name="studentinfo['name']"/>
年齡:<inputtype="text"name="studentinfo['age']"/>
.. .. ..
<!-- studentinfo是包裝對(duì)象中的屬性名
[]里邊name是存儲(chǔ)map中的key
action中將map定義在包裝對(duì)象中,以包裝對(duì)象接收數(shù)據(jù)漏麦。--!>
</td>
</tr>
Contrller方法定義如下:
public String useraddsubmit(Model model,UserVo userVo)throws Exception{
System.out.println(userVo.getStudentinfo());
}
注意:包含日期的要進(jìn)行格式轉(zhuǎn)換(放到action中)
// 使用注解進(jìn)行日期格式數(shù)據(jù)轉(zhuǎn)換
@InitBinder
private void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
// 指定日期類型及日期數(shù)據(jù)的格式
// 日期類型要和student類的birthday一致
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
@RequestParam綁定單個(gè)請(qǐng)求參數(shù)
value
:參數(shù)名字客税,即入?yún)⒌恼?qǐng)求參數(shù)名字,如value=“studentid”表示請(qǐng)求的參數(shù)區(qū)中的名字為studentid的參數(shù)的值將傳入撕贞;
required
:是否必須更耻,默認(rèn)是true,表示請(qǐng)求中一定要有相應(yīng)的參數(shù)捏膨,否則將報(bào)400錯(cuò)誤碼秧均;
defaultValue
:默認(rèn)值,表示如果請(qǐng)求中沒(méi)有同名參數(shù)時(shí)的默認(rèn)值
定義如下:
public String userlist(@RequestParam(defaultValue="2",value="group",required=true) String groupid) {
}
形參名稱為groupid
号涯,但是這里使用value="group"
限定參數(shù)名為group
目胡,所以頁(yè)面?zhèn)鬟f參數(shù)的名必須為group
。
這里通過(guò)required=true
限定groupid參數(shù)為必需傳遞链快,如果不傳遞則報(bào)400錯(cuò)誤誉己,由于使用了defaultvalue=”2”
默認(rèn)值即使不傳group參數(shù)它的值為”2”,所以頁(yè)面不傳遞group也不會(huì)報(bào)錯(cuò)域蜗,如果去掉defaultvalue=”2”
且定義required=true
則如果頁(yè)面不傳遞group
則會(huì)報(bào)錯(cuò)巨双。
@PathVariable 綁定URI 模板變量值
@PathVariable用于將請(qǐng)求URL中的模板變量映射到功能處理方法的參數(shù)上
@RequestMapping(value="/useredit/{groupid}/{userid}",method={RequestMethod.GET,RequestMethod.POST})
public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable獲取useried的值噪猾,使用model傳回頁(yè)面
model.addAttribute("groupid", groupid);
model.addAttribute("userid", userid);
return"/user/useredit";
}
如請(qǐng)求的URL 為“控制器URL/useredit/1/admin.action”,則自動(dòng)將URL中模板變量{groupid}和{userid}
綁定到@PathVariable
注解的同名參數(shù)上筑累,即入?yún)⒑?code>groupid=“1”畏妖、userid=“admin”
結(jié)果轉(zhuǎn)發(fā)
Redirect
Contrller方法返回結(jié)果重定向到一個(gè)url地址,如果方式:
return "redirect:/user/userlist.action";
redirect
方式相當(dāng)于“response.sendRedirect()”
疼阔,轉(zhuǎn)發(fā)后瀏覽器的地址欄變?yōu)檗D(zhuǎn)發(fā)后的地址,因?yàn)檗D(zhuǎn)發(fā)即執(zhí)行了一個(gè)新的request
和response
半夷。
由于新發(fā)起一個(gè)request
原來(lái)的參數(shù)在轉(zhuǎn)發(fā)時(shí)就不能傳遞到下一個(gè)url婆廊,如果要傳參數(shù)可以/user/userlist.action
后邊加參數(shù),如下:
/user/userlist.action?groupid=2&…..
forward
controller方法執(zhí)行后繼續(xù)執(zhí)行另一個(gè)controller方法巫橄。
return "forward:/user/userlist.action";
forward
方式相當(dāng)于“request.getRequestDispatcher().forward(request,response)”
淘邻,轉(zhuǎn)發(fā)后瀏覽器地址欄還是原來(lái)的地址。轉(zhuǎn)發(fā)并沒(méi)有執(zhí)行新的request和response湘换,而是和轉(zhuǎn)發(fā)前的請(qǐng)求共用一個(gè)request和response宾舅。所以轉(zhuǎn)發(fā)前請(qǐng)求的參數(shù)在轉(zhuǎn)發(fā)后仍然可以讀取到。
如下例子:
@RequestMapping("/c")
public String c(String groupid,UserVo userVo)throws Exception{
System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
return "forward:/to/d.action";
}
@RequestMapping("/d")
public String d(String groupid,UserVo userVo)throws Exception{
System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
return "success";
}
@RequestBody @ResponseBody實(shí)現(xiàn)json數(shù)據(jù)交互
@RequestBody
作用:
@RequestBody
注解用于讀取http請(qǐng)求的內(nèi)容(字符串)彩倚,通過(guò)springmvc
提供的HttpMessageConverter
接口將讀到的內(nèi)容轉(zhuǎn)換為json
筹我、xml
等格式的數(shù)據(jù)并綁定到controller
方法的參數(shù)上。
本例子應(yīng)用:
@RequestBody
注解實(shí)現(xiàn)接收http
請(qǐng)求的json
數(shù)據(jù)帆离,將jso
n數(shù)據(jù)轉(zhuǎn)換為java對(duì)象.
@ResponseBody
作用:
該注解用于將Controller
的方法返回的對(duì)象蔬蕊,通過(guò)HttpMessageConverter
接口轉(zhuǎn)換為指定格式的數(shù)據(jù)如:json,xml等,通過(guò)Response
響應(yīng)給客戶端
本例子應(yīng)用:
@ResponseBody
注解實(shí)現(xiàn)將controller
方法返回對(duì)象轉(zhuǎn)換為json
響應(yīng)給客戶端
請(qǐng)求json哥谷,響應(yīng)json實(shí)現(xiàn):
頁(yè)面上請(qǐng)求的是一個(gè)json串岸夯,頁(yè)面上實(shí)現(xiàn)時(shí)需要拼接一個(gè)json串提交到action。
Action將請(qǐng)求的json串轉(zhuǎn)為java對(duì)象们妥。SpringMVC利用@ResquesBody注解實(shí)現(xiàn).
Action將java對(duì)象轉(zhuǎn)為json猜扮,輸出到頁(yè)面。SpringMVC利用@ResponseBody注解實(shí)現(xiàn).
環(huán)境準(zhǔn)備
Springmvc
默認(rèn)用MappingJacksonHttpMessageConverter
對(duì)json
數(shù)據(jù)進(jìn)行轉(zhuǎn)換监婶,需要加入jackson
的包旅赢,如下:
配置:
在注解適配器中加入messageConverters
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
controller編寫
/**
* 請(qǐng)求json單個(gè)對(duì)象,返回json單個(gè)對(duì)象
* @param user
* @return
* @throws Exception
*/
@RequestMapping("/requestjson")
//@RequestBody接收json串自動(dòng)轉(zhuǎn)為user對(duì)象压储,@ResponseBody將user轉(zhuǎn)為json數(shù)據(jù)響應(yīng)給客戶端
Public @ResponseBody User requestjson(@RequestBody User user)throws Exception{
System.out.println(user);
return user;
}
頁(yè)面js方法編寫:
function request_json(){
//將json對(duì)象傳成字符串
var user = JSON.stringify({name: "張三", age: 3});
alert(user);
$.ajax(
{
type:'post',
url:'${pageContext.request.contextPath}/requestjson.action',
contentType:'application/json;charset=utf-8',//請(qǐng)求內(nèi)容為json
data:user,
success: function(data){
alert(data.name);
}
}
)
}
測(cè)試結(jié)果:
從上圖可以看出請(qǐng)求的數(shù)據(jù)是json格式
從上圖可以看出響應(yīng)的數(shù)據(jù)也是json格式鲜漩,json數(shù)據(jù)的內(nèi)容是從User對(duì)象轉(zhuǎn)換得來(lái)。
Form提交集惋,響應(yīng)json實(shí)現(xiàn):
采用form提交是最常用的作法孕似,通常有post和get兩種方法,響應(yīng)json數(shù)據(jù)是為了方便客戶端處理刮刑,實(shí)現(xiàn)如下:
環(huán)境準(zhǔn)備
同第一個(gè)例子
controller編寫
/
* 客戶端提交表單喉祭,服務(wù)端返回json
* @param user
* @return
* @throws Exception
/
@RequestMapping("/formsubmit")
Public @ResponseBody User formsubmit(User user)throws Exception{
System.out.println(user);
return user;
}*
頁(yè)面js方法編寫:
function formsubmit(){
var user = "name=張三&age=3";
alert(user);
$.ajax(
{
type:'post',//這里改為get也可以正常執(zhí)行
url:'${pageContext.request.contextPath}/formsubmit.action',
//ContentType沒(méi)指定將默認(rèn)為:application/x-www-form-urlencoded
data:user,
success:function(data){
alert(data.name);
}
}
)
}
從上邊的js代碼看出养渴,已去掉ContentType的定義,ContentType默認(rèn)為:application/x-www-form-urlencoded格式泛烙。
測(cè)試結(jié)果:
從上圖可以看出請(qǐng)求的數(shù)據(jù)是標(biāo)準(zhǔn)的key/value格式理卑。
從上圖可以看出響應(yīng)的數(shù)據(jù)也是json格式,json數(shù)據(jù)的內(nèi)容是從User對(duì)象轉(zhuǎn)換得來(lái)蔽氨。
<mvc:annotation-driven />配置:
注解映射器和注解適配器可以使用 <mvc:annotation-driven />
代替藐唠。
<mvc:annotation-driven />
默認(rèn)注冊(cè)了注解映射器和注解適配器等bean。
如下:
以下配置可用 <mvc:annotation-driven />
代替:
<!--注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
15.攔截器
定義
Spring Web MVC 的處理器攔截器類似于Servlet 開(kāi)發(fā)中的過(guò)濾器Filter鹉究,用于對(duì)處理器進(jìn)行預(yù)處理和后處理宇立。
SpringMVC攔截器是針對(duì)mapping配置的攔截器
攔截器定義
實(shí)現(xiàn)HandlerInterceptor
接口,如下:
Public class HandlerInterceptor1 implements HandlerInterceptor{
/**
* controller執(zhí)行前調(diào)用此方法
* 返回true表示繼續(xù)執(zhí)行自赔,返回false中止執(zhí)行
* 這里可以加入登錄校驗(yàn)妈嘹、權(quán)限攔截等
*/
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Return false;
}
/**
* controller執(zhí)行后但未返回視圖前調(diào)用此方法
* 這里可在返回用戶前對(duì)模型數(shù)據(jù)進(jìn)行加工處理,比如這里加入公用信息以便頁(yè)面顯示
*/
@Override
Public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/**
* controller執(zhí)行后且視圖返回后調(diào)用此方法
* 這里可得到執(zhí)行controller時(shí)的異常信息
* 這里可記錄操作日志绍妨,資源清理等
*/
@Override
Public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
攔截器配置
針對(duì)某種mapping配置攔截器
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
針對(duì)所有mapping配置全局?jǐn)r截器
<!--攔截器 -->
<mvc:interceptors>
<!--多個(gè)攔截器,順序執(zhí)行 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
正常流程測(cè)試
代碼:
定義兩個(gè)攔截器分別為:HandlerInterceptor1和HandlerInteptor2
润脸,每個(gè)攔截器的preHandler
方法都返回true
。
運(yùn)行流程
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor2..postHandle..
HandlerInterceptor1..postHandle..
HandlerInterceptor2..afterCompletion..
HandlerInterceptor1..afterCompletion..
中斷流程測(cè)試
代碼:
定義兩個(gè)攔截器分別為:HandlerInterceptor1
和HandlerInteptor2
他去。
運(yùn)行流程
HandlerInterceptor1
的preHandler
方法返回false
,HandlerInterceptor2
返回true
毙驯,運(yùn)行流程如下:
HandlerInterceptor1..preHandle..
從日志看出第一個(gè)攔截器的preHandler方法返回false
后第一個(gè)攔截器只執(zhí)行了preHandler方法,其它兩個(gè)方法沒(méi)有執(zhí)行灾测,第二個(gè)攔截器的所有方法不執(zhí)行尔苦,且controller也不執(zhí)行了。
HandlerInterceptor1
的preHandler
方法返回true
行施,HandlerInterceptor2
返回false
允坚,運(yùn)行流程如下:
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor1..afterCompletion..
從日志看出第二個(gè)攔截器的preHandler
方法返回false
后第一個(gè)攔截器的ostHandler
沒(méi)有執(zhí)行,第二個(gè)攔截器的postHandler
和afterCompletion
沒(méi)有執(zhí)行蛾号,且controller
也不執(zhí)行了稠项。
總結(jié):
preHandle
按攔截器定義順序調(diào)用
postHandler
按攔截器定義逆序調(diào)用
afterCompletion
按攔截器定義逆序調(diào)用
postHandler
在攔截器鏈內(nèi)所有攔截器返成功調(diào)用
afterCompletion
只有preHandle
返回true
才調(diào)用
攔截器應(yīng)用
用戶身份認(rèn)證
Public class LoginInterceptor implements HandlerInterceptor{
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//如果是登錄頁(yè)面則放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true;
}
HttpSession session = request.getSession();
//如果用戶已登錄也放行
if(session.getAttribute("user")!=null){
return true;
}
//用戶沒(méi)有登錄挑戰(zhàn)到登錄頁(yè)面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}