Spring MVC 框架

1.Spring web mvc介紹

Spring web mvcStruts2都屬於表現層的框架,它是Spring框架的一部分,我們可以從Spring的整體結構中看得出來:

這裏寫圖片描述

2. Web mvc

  1. 用戶發起request請求至控制器(Controller)
    控制接收用戶請求的數據,委託給模型進行處理。

  2. 控制器通過模型(Model)處理數據並得到處理結果
    模型通常是指業務邏輯。

  3. 控制器將模型數據在視圖(View)中展示
    web中模型無法將數據直接在視圖上顯示,需要通過控制器完成。如果在C/S應用中模型是可以將數據在視圖中展示的。
  4. 控制器將視圖response響應給用戶
    通過視圖展示給用戶要的數據或處理結果。

image

3.Spring web mvc 架構

架構圖

這裏寫圖片描述

流程
1. 用戶發送請求至前端控制器DispatcherServlet
2. DispatcherServlet收到請求調用HandlerMapping處理器映射器。
3. 處理器映射器找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet
4. DispatcherServlet調用HandlerAdapter處理器適配器
5. HandlerAdapter經過適配調用具體的處理器(Controller,也叫後端控制器)。
6.
Controller執行完成返回ModelAndView
7.
HandlerAdaptercontroller執行結果ModelAndView返回給DispatcherServlet.
8.
DispatcherServletModelAndView傳給ViewReslover視圖解析器
9.
ViewReslover解析後返回具體View
10.
DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。
11.
DispatcherServlet`響應用戶

組件說明:

++以下組件通常使用框架提供實現:++

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包;

prefixsuffix:查找視圖頁面的前綴和後綴,最終視圖的址爲:前綴+邏輯視圖名+後綴,邏輯視圖名需要在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! ”就表明我們成功了。

總結:

主要進行如下操作:

  1. 前端控制器DispatcherServlet配置
    加載springmvc的配置文件
  2. HandlerMapping配置
  3. HandlerAdapter配置
  4. ViewResolver配置
    前綴和後綴
  5. 後端控制器編寫
  6. 後端控制器配置
  7. 視圖編寫

從上邊的步驟可以看出,通常情況下我們只需要編寫後端控制器和視圖。

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請求封裝成HttpServletResquestHttpServletResponse對象,和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工程,註解開發需要修改handlermapperhandlMapperAdapter,如下:

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不同

  1. springmvc的入口是一個servlet即前端控制器,而struts2入口是一個filter過慮器。
  2. springmvc是基於方法開發,請求參數傳遞到方法的形參,可以設計爲單例或多例(建議單例),struts2是基於類開發,傳遞參數是通過類的屬性,只能設計爲多例。
  3. 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()”,轉發後瀏覽器的地址欄變爲轉發後的地址,因爲轉發即執行了一個新的requestresponse
由於新發起一個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接口將讀到的內容轉換爲jsonxml等格式的數據並綁定到controller方法的參數上。

本例子應用:

@RequestBody註解實現接收http請求的json數據,將json數據轉換爲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默認用MappingJacksonHttpMessageConverterjson數據進行轉換,需要加入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..

中斷流程測試

代碼:
定義兩個攔截器分別爲:HandlerInterceptor1HandlerInteptor2

運行流程

HandlerInterceptor1preHandler方法返回false,HandlerInterceptor2返回true,運行流程如下:

HandlerInterceptor1..preHandle..

從日誌看出第一個攔截器的preHandler方法返回false後第一個攔截器只執行了preHandler方法,其它兩個方法沒有執行,第二個攔截器的所有方法不執行,且controller也不執行了。

HandlerInterceptor1preHandler方法返回trueHandlerInterceptor2返回false,運行流程如下:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

從日誌看出第二個攔截器的preHandler方法返回false後第一個攔截器的ostHandler沒有執行,第二個攔截器的postHandlerafterCompletion沒有執行,且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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章