1.Spring web mvc介紹
Spring web mvc
和Struts2
都屬於表現層的框架,它是Spring框架的一部分,我們可以從Spring的整體結構中看得出來:
2. Web mvc
用戶發起
request
請求至控制器(Controller)
控制接收用戶請求的數據,委託給模型進行處理。控制器通過模型(Model)處理數據並得到處理結果
模型通常是指業務邏輯。- 控制器將模型數據在視圖(View)中展示
web中模型無法將數據直接在視圖上顯示,需要通過控制器完成。如果在C/S應用中模型是可以將數據在視圖中展示的。 - 控制器將視圖response響應給用戶
通過視圖展示給用戶要的數據或處理結果。
3.Spring web mvc 架構
架構圖
流程
1. 用戶發送請求至前端控制器DispatcherServlet
。
2. DispatcherServlet
收到請求調用HandlerMapping
處理器映射器。
3. 處理器映射器找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet
。
4. DispatcherServle
t調用HandlerAdapter
處理器適配器
5. HandlerAdapter經過適配調用具體的處理器(Controller,也叫後端控制器)。
Controller
6.執行完成返回
ModelAndView
HandlerAdapter
7.將
controller執行結果
ModelAndView返回給
DispatcherServlet.
DispatcherServlet
8.將
ModelAndView傳給
ViewReslover視圖解析器
ViewReslover
9.解析後返回具體View
DispatcherServlet
10.根據
View進行渲染視圖(即將模型數據填充至視圖中)。
DispatcherServlet`響應用戶
11.
組件說明:
++以下組件通常使用框架提供實現:++
DispatcherServlet
:作爲前端控制器,整個流程控制的中心,控制其它組件執行,統一調度,降低組件之間的耦合性,提高每個組件的擴展性。
HandlerMapping
:通過擴展處理器映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
HandlAdapter
:通過擴展處理器適配器,支持更多類型的處理器。
ViewResolver
:通過擴展視圖解析器,支持更多類型的視圖解析,例如:jsp、freemarker、pdf、excel等。
++下邊兩個組件通常情況下需要開發++:
Handler
:處理器,即後端控制器用controller表示。
View
:視圖,即展示給用戶的界面,視圖中通常需要標籤語言展示模型數據。
4.開發環境準備
本教程使用Eclipse+tomcat7開發
詳細參考“Eclipse開發環境配置-indigo.docx”文檔
5.第一個springmvc工程
第一步:建立一個Web項目
在eclipse下創建動態web工程springmvc_01。
第二步:導入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隨服務啓動
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
// 以.action結尾的請求交給DispatcherServlet處理
<url-pattern>*.action</url-pattern>
</servlet-mapping>
load-on-startup
:表示servlet隨服務啓動;
url-pattern
:*.action的請交給DispatcherServlet處理。
contextConfigLocation
:指定springmvc配置的加載位置,如果不指定則默認加載WEB-INF/[DispatcherServlet 的Servlet 名字]-servlet.xml。
第四步:springmvc配置文件
Springmvc默認加載WEB-INF/[前端控制器的名字]-servlet.xml,也可以在前端控制器定義處指定加載的配置文件,如下:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
如上代碼,通過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 ">
<!-- 處理器映射器 -->
<!-- 根據bean的name進行查找Handler 將action的url配置在bean的name中 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
BeanNameUrlHandlerMapping
:表示將定義的Bean名字作爲請求的url,需要將編寫的controller在spring容器中進行配置,且指定bean的name爲請求的url,且必須以.action結尾。
第六步:配置處理器適配器
在springmvc-servlet.xml文件配置如下:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
SimpleControllerHandlerAdapter
:即簡單控制器處理適配器,所有實現了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模板頁面需要使用JSTL標籤庫,所以classpath必須包含jstl的相關jar包;
prefix
和suffix
:查找視圖頁面的前綴和後綴,最終視圖的址爲:前綴+邏輯視圖名+後綴,邏輯視圖名需要在controller中返回ModelAndView指定,比如邏輯視圖名爲hello,則最終返回的jsp視圖地址“WEB-INF/jsp/hello.jsp”
第八步:後端控制器開發
後端控制器即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();
// 添加模型數據
mv.addObject("message", "Hello World!");
// 設置邏輯視圖名,最終視圖地址=前綴+邏輯視圖名+後綴
mv.setViewName("hello");
return mv;
}
}
org.springframework.web.servlet.mvc.Controller
:處理器必須實現Controller 接口。
ModelAndView
:包含了模型數據及邏輯視圖名
第九步:後端控制器配置
在springmvc-servlet.xml文件配置如下:
<bean name="/hello.action" class="springmvc.action.HelloWorldController"/>
name="/hello.action"
:前邊配置的BeanNameUrlHandlerMapping,表示如過請求的URL爲“上下文/hello.action”,則將會交給該Bean進行處理。
第十步:視圖開發
創建/WEB-INF/jsp/hello.jsp視圖頁面:
<%@ 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>第一個程序</title>
</head>
<body>
<!-- 顯示一行信息hello world!!!! -->
${message}
</body>
</html>
${message}
:表示顯示由HelloWorldController處理器傳過來的模型數據。
第十一步:部署在tomcat測試
通過請求:http://localhost:8080/springmvc_01/hello.action,如果頁面輸出“Hello World! ”就表明我們成功了。
總結:
主要進行如下操作:
- 前端控制器DispatcherServlet配置
加載springmvc的配置文件 - HandlerMapping配置
- HandlerAdapter配置
- ViewResolver配置
前綴和後綴 - 後端控制器編寫
- 後端控制器配置
- 視圖編寫
從上邊的步驟可以看出,通常情況下我們只需要編寫後端控制器和視圖。
6.HandlerMapping處理器映射器
HandlerMapping 給前端控制器返回一個HandlerExecutionChain 對象(包含一個Handler (後端控制器)對象、多個HandlerInterceptor 攔截器)對象。
BeanNameUrlHandlerMapping
beanName Url映射器
<!—beanName Url映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
將後端控制器的bean name作爲請求的url。
SimpleUrlHandlerMapping
<!—簡單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>
可定義多個url映射至一個後端控制器,hello_controller爲後端控制器bean的id。
7.HandlerAdapter處理器適配器
HandlerAdapter會把後端控制器包裝爲一個適配器,支持多種類型的控制器開發,這裏使用了適配器設計模式。
SimpleControllerHandlerAdapter
簡單控制器處理器適配器.
所有實現了org.springframework.web.servlet.mvc.Controller
接口的Bean作爲Springmvc的後端控制器。
適配器配置如下:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
HttpRequestHandlerAdapter
HTTP請求處理器適配器.
HTTP請求處理器適配器將http請求封裝成HttpServletResquest
和HttpServletResponse
對象,和servlet
接口類似。
適配器配置如下:
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
Controller實現如下:
public class HelloWorldController2 implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("message", "HelloWorld!");
request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request, response);
// 也可以自定義響應內容
//response.setCharacterEncoding("utf-8");
//response.getWriter().write("HelloWorld!");
}
}
從上邊可以看出此適配器器的controller方法沒有返回ModelAndView
,可通過response
修改定義響應內容。
8.Controller控制器
AbstractCommandController(命令控制器)
該控制器能把請求參數封裝到一個命令對象(模型對象)中。
public class MyCommandController extends AbstractCommandController {
/**
* 通過構造函數設置命令對象
*/
public MyCommandController(){
this.setCommandClass(Student.class);
this.setCommandName("student");//不是必須設置
}
@Override
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
Student student = (Student)command;
System.out.println(student);
Return null;
}
/**
* 字符串轉換爲日期屬性編輯器
*/
@Override
Protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
}
public class 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.問題解決
日期格式化
在controller註冊屬性編輯器:
/**
* 註冊屬性編輯器(字符串轉換爲日期)
*/
@InitBinder
public void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
Post時中文亂碼
在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請求亂碼問題。
對於get請求中文參數出現亂碼解決方法有兩個:
修改tomcat配置文件添加編碼與工程編碼一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一種方法對參數進行重新編碼:
String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默認編碼,需要將tomcat編碼後的內容按utf-8編碼
10.註解開發
第一個例子
創建工程的步驟同第一個springmvc工程,註解開發需要修改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 />
默認註冊了註解映射器和註解適配器等bean。
HelloWorldController編寫:
@Controller
public class HelloWorldController {
@RequestMapping(value="/hello")
public String hello(Model model)throws Exception{
model.addAttribute("message", "HelloWorld!");
return"hello";
}
}
註解描述:
@Controller
:用於標識是處理器類
@RequestMapping
:請求到處理器功能方法的映射規則
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的入口是一個servlet即前端控制器,而struts2入口是一個filter過慮器。
- springmvc是基於方法開發,請求參數傳遞到方法的形參,可以設計爲單例或多例(建議單例),struts2是基於類開發,傳遞參數是通過類的屬性,只能設計爲多例。
- Struts採用值棧存儲請求和響應的數據,通過OGNL存取數據, springmvc通過參數解析器是將request請求內容解析,並給方法形參賦值,將數據和視圖封裝成ModelAndView對象,最後又將ModelAndView中的模型數據通過reques域傳輸到頁面。Jsp視圖解析器默認使用jstl。
注意:
不要在action類中定義方法所用到的變量,變量要定義到方法體中。因爲方法是在棧內存中,不會導致線程問題。
12.@Controller
標識該類爲控制器類,@controller
、@service
、@repository
分別對應了web應用三層架構的組件即控制器、服務接口、數據訪問接口。
13.@RequestMapping
URL路徑映射
@RequestMapping(value=”/user”)或@RequestMapping(“/user”)
根路徑+子路徑
根路徑:
@RequestMapping
放在類名上邊,如下:
@Controller
@RequestMapping("/user")
子路徑:
@RequestMapping
放在方法名上邊,如下:
@RequestMapping("/useradd")
public String useradd(….
URI 模板模式映射
@RequestMapping(value=”/useredit/{userId}”):{×××}佔位符,請求的URL可以是“/useredit/001”或“/useredit/abc”,通過在方法中使用@PathVariable 獲取{×××}中的×××變量。
@RequestMapping("/useredit/{userid}")
public String useredit(@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable獲取useried的值,使用model傳回頁面
model.addAttribute("userid", userid);
return"/user/useredit";
}
實現restFul,所有的url都是一個資源的鏈接,有利於搜索引擎對網址收錄。
多個佔位符:
@RequestMapping("/useredit/{groupid}/{userid}")
public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable獲取useried的值,使用model傳回頁面
model.addAttribute("groupid", groupid);
model.addAttribute("userid", userid);
return"/user/useredit";
}
頁面:
<td>
<a href="${pageContext.request.contextPath}/user/editStudent/${student.id}/${student.groupId}/${student.sex}.action">修改</a>
</td>
請求方法限定
限定GET方法
@RequestMapping(method = RequestMethod.GET)
如果通過Post訪問則報錯:
HTTP Status 405 - Request method ‘POST’ not supported
例如:
@RequestMapping(value="/useredit/{userid}",method=RequestMethod.GET)
限定POST方法
@RequestMapping(method = RequestMethod.POST)
如果通過Post訪問則報錯:
HTTP Status 405 - Request method ‘GET’ not supported
GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
14.請求數據綁定(重點)
Controller方法通過形參接收頁面傳遞的參數。
如下:
@RequestMapping("/userlist")
public String userlist(
HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Model model
)
默認支持的參數類型
HttpServletRequest
通過request對象獲取請求信息
HttpServletResponse
通過response處理響應信息
HttpSession
通過session對象得到session中存放的對象
session.setAttribute("userinfo", student);
session.invalidate(); // 使session失效
Model
通過model向頁面傳遞數據,如下:
model.addAttribute("user", new User("李四"));
頁面通過${user.XXXX}獲取user對象的屬性值。
命令/表單對象
自動將請求參數綁定到功能處理方法的命令/表單對象上。
Controller方法通過形參接收命令/表單對象。
Java基本數據類型
布爾型:
頁面如下:
<tr>
<td>用戶狀態:</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對象
頁面上以pojo對象中屬性名稱命名:
將pojo對象中的屬性名於傳遞進來的屬性名對應,如果傳進來的參數名稱和對象中的屬性名稱一致則將參數值設置在pojo對象中
頁面定義如下;
<input type="text" name="age"/>
<input type="text" name="birthday"/>
Contrller方法定義如下:
public String useraddsubmit(Model model,User user)throws Exception{
System.out.println(user);
}
頁面上以pojo對象名點屬性名命名:
如果採用類似struts中對象.屬性的方式命名,需要將pojo對象作爲一個包裝對象的屬性,action中以該包裝對象作爲形參。
包裝對象定義如下:
public class UserVo {
private User user;
public User getUser() {
return user;
}
Public void setUser(User user) {
this.user = user;
}
}
頁面定義:
<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());
}
字符串數組
使用情景:頁面提交批量數據以數組接受
頁面定義如下:
頁面選中多個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中存放對象,並將定義的List放在包裝類中,action使用包裝對象接收。
List中對象:
成績對象
public class StudentScore {
private String coursename;//課程名稱
private Float score;//成績
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;//成績
//get/set方法..
}
包裝類中定義List對象,並添加get/set方法如下:
private List<StudentScore> scores;//成績
頁面:
<tr>
<td>課程成績:</td>
<td>
課程名:<input type="text"name="scores[0].coursename"/>成績:<input type="text"name="scores[0].score"/><br/>
課程名:<input type="text"name="scores[1].coursename"/>成績:<input type="text"name="scores[1].score"/><br/>
課程名:<input type="text"name="scores[2].coursename"/>成績:<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對象,並添加get/set方法,action使用包裝對象接收。
包裝類中定義Map對象如下:
Public class UserVo {
private Map<String, Object>studentinfo = new HashMap<String, Object>();
//get/set方法..
}
頁面定義如下:
<tr>
<td>學生信息:</td>
<td>
姓名:<inputtype="text"name="studentinfo['name']"/>
年齡:<inputtype="text"name="studentinfo['age']"/>
.. .. ..
<!-- studentinfo是包裝對象中的屬性名
[]裏邊name是存儲map中的key
action中將map定義在包裝對象中,以包裝對象接收數據。--!>
</td>
</tr>
Contrller方法定義如下:
public String useraddsubmit(Model model,UserVo userVo)throws Exception{
System.out.println(userVo.getStudentinfo());
}
注意:包含日期的要進行格式轉換(放到action中)
// 使用註解進行日期格式數據轉換
@InitBinder
private void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
// 指定日期類型及日期數據的格式
// 日期類型要和student類的birthday一致
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
@RequestParam綁定單個請求參數
value
:參數名字,即入參的請求參數名字,如value=“studentid”表示請求的參數區中的名字爲studentid的參數的值將傳入;
required
:是否必須,默認是true,表示請求中一定要有相應的參數,否則將報400錯誤碼;
defaultValue
:默認值,表示如果請求中沒有同名參數時的默認值
定義如下:
public String userlist(@RequestParam(defaultValue="2",value="group",required=true) String groupid) {
}
形參名稱爲groupid
,但是這裏使用value="group"
限定參數名爲group
,所以頁面傳遞參數的名必須爲group
。
這裏通過required=true
限定groupid參數爲必需傳遞,如果不傳遞則報400錯誤,由於使用了defaultvalue=”2”
默認值即使不傳group參數它的值爲”2”,所以頁面不傳遞group也不會報錯,如果去掉defaultvalue=”2”
且定義required=true
則如果頁面不傳遞group
則會報錯。
@PathVariable 綁定URI 模板變量值
@PathVariable用於將請求URL中的模板變量映射到功能處理方法的參數上
@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傳回頁面
model.addAttribute("groupid", groupid);
model.addAttribute("userid", userid);
return"/user/useredit";
}
如請求的URL 爲“控制器URL/useredit/1/admin.action”,則自動將URL中模板變量{groupid}和{userid}
綁定到@PathVariable
註解的同名參數上,即入參後groupid=“1”
、userid=“admin”
結果轉發
Redirect
Contrller方法返回結果重定向到一個url地址,如果方式:
return "redirect:/user/userlist.action";
redirect
方式相當於“response.sendRedirect()”
,轉發後瀏覽器的地址欄變爲轉發後的地址,因爲轉發即執行了一個新的request
和response
。
由於新發起一個request
原來的參數在轉發時就不能傳遞到下一個url,如果要傳參數可以/user/userlist.action
後邊加參數,如下:
/user/userlist.action?groupid=2&…..
forward
controller方法執行後繼續執行另一個controller方法。
return "forward:/user/userlist.action";
forward
方式相當於“request.getRequestDispatcher().forward(request,response)”
,轉發後瀏覽器地址欄還是原來的地址。轉發並沒有執行新的request和response,而是和轉發前的請求共用一個request和response。所以轉發前請求的參數在轉發後仍然可以讀取到。
如下例子:
@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實現json數據交互
@RequestBody
作用:
@RequestBody
註解用於讀取http請求的內容(字符串),通過springmvc
提供的HttpMessageConverter
接口將讀到的內容轉換爲json
、xml
等格式的數據並綁定到controller
方法的參數上。
本例子應用:
@RequestBody
註解實現接收http
請求的json
數據,將jso
n數據轉換爲java對象.
@ResponseBody
作用:
該註解用於將Controller
的方法返回的對象,通過HttpMessageConverter
接口轉換爲指定格式的數據如:json,xml等,通過Response
響應給客戶端
本例子應用:
@ResponseBody
註解實現將controller
方法返回對象轉換爲json
響應給客戶端
請求json,響應json實現:
頁面上請求的是一個json串,頁面上實現時需要拼接一個json串提交到action。
Action將請求的json串轉爲java對象。SpringMVC利用@ResquesBody註解實現.
Action將java對象轉爲json,輸出到頁面。SpringMVC利用@ResponseBody註解實現.
環境準備
Springmvc
默認用MappingJacksonHttpMessageConverter
對json
數據進行轉換,需要加入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編寫
/**
* 請求json單個對象,返回json單個對象
* @param user
* @return
* @throws Exception
*/
@RequestMapping("/requestjson")
//@RequestBody接收json串自動轉爲user對象,@ResponseBody將user轉爲json數據響應給客戶端
Public @ResponseBody User requestjson(@RequestBody User user)throws Exception{
System.out.println(user);
return user;
}
頁面js方法編寫:
function request_json(){
//將json對象傳成字符串
var user = JSON.stringify({name: "張三", age: 3});
alert(user);
$.ajax(
{
type:'post',
url:'${pageContext.request.contextPath}/requestjson.action',
contentType:'application/json;charset=utf-8',//請求內容爲json
data:user,
success: function(data){
alert(data.name);
}
}
)
}
測試結果:
從上圖可以看出請求的數據是json格式
從上圖可以看出響應的數據也是json格式,json數據的內容是從User對象轉換得來。
Form提交,響應json實現:
採用form提交是最常用的作法,通常有post和get兩種方法,響應json數據是爲了方便客戶端處理,實現如下:
環境準備
同第一個例子
controller編寫
**/**
* 客戶端提交表單,服務端返回json
* @param user
* @return
* @throws Exception
*/
@RequestMapping("/formsubmit")
Public @ResponseBody User formsubmit(User user)throws Exception{
System.out.println(user);
return user;
}**
頁面js方法編寫:
function formsubmit(){
var user = "name=張三&age=3";
alert(user);
$.ajax(
{
type:'post',//這裏改爲get也可以正常執行
url:'${pageContext.request.contextPath}/formsubmit.action',
//ContentType沒指定將默認爲:application/x-www-form-urlencoded
data:user,
success:function(data){
alert(data.name);
}
}
)
}
從上邊的js代碼看出,已去掉ContentType的定義,ContentType默認爲:application/x-www-form-urlencoded格式。
測試結果:
從上圖可以看出請求的數據是標準的key/value格式。
從上圖可以看出響應的數據也是json格式,json數據的內容是從User對象轉換得來。
配置:
註解映射器和註解適配器可以使用 <mvc:annotation-driven />
代替。
<mvc:annotation-driven />
默認註冊了註解映射器和註解適配器等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 開發中的過濾器Filter,用於對處理器進行預處理和後處理。
SpringMVC攔截器是針對mapping配置的攔截器
攔截器定義
實現HandlerInterceptor
接口,如下:
public class HandlerInterceptor1 implements HandlerInterceptor{
/**
* controller執行前調用此方法
* 返回true表示繼續執行,返回false中止執行
* 這裏可以加入登錄校驗、權限攔截等
*/
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Return false;
}
/**
* controller執行後但未返回視圖前調用此方法
* 這裏可在返回用戶前對模型數據進行加工處理,比如這裏加入公用信息以便頁面顯示
*/
@Override
Public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/**
* controller執行後且視圖返回後調用此方法
* 這裏可得到執行controller時的異常信息
* 這裏可記錄操作日誌,資源清理等
*/
@Override
Public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
攔截器配置
針對某種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"/>
針對所有mapping配置全局攔截器
<!--攔截器 -->
<mvc:interceptors>
<!--多個攔截器,順序執行 -->
<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>
正常流程測試
代碼:
定義兩個攔截器分別爲:HandlerInterceptor1和HandlerInteptor2
,每個攔截器的preHandler
方法都返回true
。
運行流程
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor2..postHandle..
HandlerInterceptor1..postHandle..
HandlerInterceptor2..afterCompletion..
HandlerInterceptor1..afterCompletion..
中斷流程測試
代碼:
定義兩個攔截器分別爲:HandlerInterceptor1
和HandlerInteptor2
。
運行流程
HandlerInterceptor1
的preHandler
方法返回false
,HandlerInterceptor2
返回true
,運行流程如下:
HandlerInterceptor1..preHandle..
從日誌看出第一個攔截器的preHandler方法返回false
後第一個攔截器只執行了preHandler方法,其它兩個方法沒有執行,第二個攔截器的所有方法不執行,且controller也不執行了。
HandlerInterceptor1
的preHandler
方法返回true
,HandlerInterceptor2
返回false
,運行流程如下:
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor1..afterCompletion..
從日誌看出第二個攔截器的preHandler
方法返回false
後第一個攔截器的ostHandler
沒有執行,第二個攔截器的postHandler
和afterCompletion
沒有執行,且controller
也不執行了。
總結:
preHandle
按攔截器定義順序調用
postHandler
按攔截器定義逆序調用
afterCompletion
按攔截器定義逆序調用
postHandler
在攔截器鏈內所有攔截器返成功調用
afterCompletion
只有preHandle
返回true
才調用
攔截器應用
用戶身份認證
public class LoginInterceptor implements HandlerInterceptor{
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//如果是登錄頁面則放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true;
}
HttpSession session = request.getSession();
//如果用戶已登錄也放行
if(session.getAttribute("user")!=null){
return true;
}
//用戶沒有登錄挑戰到登錄頁面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}