深入學習SpringMVC

ki16發表於2019-07-20

  

1.什麼是SpringMVC?

  SpringMVC是Spring框架內建的MVC的實現。SpringMVC就是一個Spring內建的MVC框架。
MVC框架,它解決WEB開發中常見的問題(引數接收、檔案上傳、表單驗證、國際化等等),而且使用簡單,與Spring無縫整合。 支援 RESTful風格的 URL 請求 。
採用了鬆散耦合可插拔元件結構,比其他 MVC 框架更具擴充套件性和靈活性。

2.SpringMVC底層實現

  在沒有使用SpringMVC之前我們都是使用的Servlet在做Web開發。但是使用Servlet開發在接受請求資料引數,資料共享,頁面跳轉等操作相對比較複雜。
SpringMVC底層就是的Servlet,SpringMVC就是對Servlet進行更深層次的封裝。

3.SpringMVC入門示例

  1.匯入jar包

  

 

  2.編寫Controller控制器(與以前servlet類似)
  建立一個類實現Controller即可

public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            
        ModelAndView mv = new ModelAndView(); //ModelAndView物件封裝了Model資料和View資訊

        mv.addObject("username", "張三");// 共享資料

        mv.setViewName("/WEB-INF/hello.jsp");// 設定跳轉頁面

        return mv;
    }
}

  3.在springmvc.xml配置 Controller

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        ">
        <!--配置springMVC控制器
        name : 給當前控制器取的一個名字,相當於Servlet中的資源名稱,以便瀏覽器訪問,必須以斜槓/開頭,
            建議使用 name屬性,不要使用id,因為早期版本 id 不支援特殊字元 如 /斜槓
      -->
    <bean name="/hello" class="com.gjs.controller.HelloController"/>
</beans>

  4.在web.xml中配置前端控制器關聯springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>springMVC1-入門</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 配置springmvc框架,web.xml是專案入口,訪問專案最先進入 web.xml -->
  
  <!-- 
      配置springmvc的前端控制器(總控)
      配置前端控制器以後,使用者瀏覽器的所有請求全部走SpringMVC,如果不配(預設走servlet)
       springmvc底層是基於servlet
   -->
   <servlet>
        <servlet-name>MVC</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
        
        <!-- 讀取配置檔案 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 是否在啟動時載入控制器
              1:是,在伺服器啟動以後立即建立物件
              -1:否(預設),使用者第一次訪問時候才建立-->
        <load-on-startup>1</load-on-startup>
   </servlet>
   
   <servlet-mapping>
           <servlet-name>MVC</servlet-name>
           <url-pattern>/</url-pattern>
   </servlet-mapping>
</web-app>

  5.配置到Tomcat上並啟動

  6.結果

  

4.SpringMVC的全註解開發(一般都用註解不會用xml方式)

  4.1 SpringMVC中的常用註解

  

  4.2 SpringMVC使用註解步驟

    1.匯入相關jar包

    

     2.在springmvc.xml中配置註解掃描及MVC註解支援

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        
        ">
        <!-- 配置要進行註解包掃描的位置 -->
        <context:component-scan base-package="com.gjs"/>
        <!-- 開啟MVC註解支援, 同時還支援返回json資料-->
        <mvc:annotation-driven/>
</beans>

    3.在web.xml中配置前端控制器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>springMVC1-入門</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 配置springmvc框架,web.xml是專案入口,訪問專案最先進入 web.xml -->
  
  <!-- 
      配置springmvc的前端控制器(總控)
      配置前端控制器以後,使用者瀏覽器的所有請求全部走SpringMVC,如果不配(預設走servlet)
       springmvc底層是基於servlet
   -->
   <servlet>
        <servlet-name>MVC</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
        
        <!-- 讀取配置檔案 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 是否啟動時載入,1:是,-1:否-->
        <load-on-startup>1</load-on-startup>
   </servlet>
   
   <servlet-mapping>
           <servlet-name>MVC</servlet-name>
           <url-pattern>/</url-pattern>
   </servlet-mapping>
</web-app>

    4.Controller控制器

package com.gjs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller // 宣告當前類為控制器
public class HelloController {

    @RequestMapping("hello")
    public ModelAndView hello() {

        ModelAndView mv = new ModelAndView();// ModelAndView物件封裝了Model資料和View資訊

        mv.addObject("username", "李四");// 共享資料

        mv.setViewName("/WEB-INF/hello.jsp");// 設定跳轉頁面

        return mv;
    }

}

5.SpringMVC執行流程和原理

  5.1SpringMVC流程:

01、使用者傳送出請求到前端控制器DispatcherServlet。
02、DispatcherServlet收到請求呼叫HandlerMapping(處理器對映器)。
03、HandlerMapping找到具體的處理器(可查詢xml配置或註解配置),生成處理器物件及處理器攔截器(如果有),再一起返回給DispatcherServlet。
04、DispatcherServlet呼叫HandlerAdapter(處理器介面卡)。
05、HandlerAdapter經過適配呼叫具體的處理器(Handler/Controller)。
06、Controller執行完成返回ModelAndView物件。
07、HandlerAdapter將Controller執行結果ModelAndView返回給DispatcherServlet。
08、DispatcherServlet將ModelAndView傳給ViewReslover(檢視解析器)。
09、ViewReslover解析後返回具體View(檢視)。
10、DispatcherServlet根據View進行渲染檢視(即將模型資料填充至檢視中)。
11、DispatcherServlet響應使用者。

  5.2 涉及元件分析:

1、前端控制器DispatcherServlet(不需要程式設計師開發),由框架提供,在web.xml中配置。
作用:接收請求,響應結果,相當於轉發器,中央處理器。

2、處理器對映器HandlerMapping(不需要程式設計師開發),由框架提供。
作用:根據請求的url查詢Handler(處理器/Controller),可以通過XML和註解方式來對映。

3、處理器介面卡HandlerAdapter(不需要程式設計師開發),由框架提供。
作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler。

4、處理器Handler(也稱之為Controller,需要工程師開發)
注意:編寫Handler時按照HandlerAdapter的要求去做,這樣介面卡才可以去正確執行Handler。
作用:接受使用者請求資訊,呼叫業務方法處理請求,也稱之為後端控制器。

5、檢視解析器ViewResolver(不需要程式設計師開發),由框架提供
作用:進行檢視解析,把邏輯檢視名解析成真正的物理檢視。
SpringMVC框架支援多種View檢視技術,包括:jstlView、freemarkerView、pdfView等。

6、檢視View(需要工程師開發)
作用:把資料展現給使用者的頁面
View是一個介面,實現類支援不同的View技術(jsp、freemarker、pdf等)

具體元件的配置相關,請查閱 spring-webmvc-4.3.2.RELEASE.jar 包下面 org/springframework/web/servlet/DispatcherServlet.properties 的相關配置

6.對靜態資源訪問

  按照第3和第4點的配置,會出現靜態資源無法訪問的情況(如html頁面)。原因是在web.xml中配置的前端控制器的對映路徑(<url-pattern>)為“/”,而Tomcat中對靜態資源處理的servlet的的對映路徑也是“/”。也就是說,我們配置SpringMVC中的DispatcherServlet的對映路徑覆蓋了Tomcat預設對靜態資源的處理的路徑。
  Tomcat部分原始碼:

<servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

  解決方案:
    方案一:如果SpringMVC要配置為/,那麼就得設定Dispatcherservlet對靜態資源進行支援。
    在SpringMVC的配置檔案中新增以下程式碼開啟對靜態資源的訪問

 <mvc:default-servlet-handler/>

    

    方案二:SpringMVC對映路徑不要配置為/ (也不要配置為/* ,/*會匹配所有url)
    實際開發中一般推薦使用 *.字尾 如 *.do *.action 

<servlet-mapping>
           <servlet-name>MVC</servlet-name>
           <url-pattern>*.do</url-pattern>
   </servlet-mapping>

    注:訪問時以.do結尾的url訪問,配置方法的對映路徑時可以不加.do(建議不加)

7.Spring請求響應

  7.1.@RequestMapping

@RequestMapping註解主要用於設定SpringMVC請求的對映路徑
所謂的對映路徑,就是匹配請求路徑和執行方法關係的路徑.

請求路徑:http://localhost:8080/springmvc/method1.do
對映路徑:@RequestMapping(value="/method1") (“value=”可以省略)

@RequestMapping 用於貼在控制器的類上或者方法上面
如果是貼在控制器的類上面,那麼在訪問這個類的方法之前必須先加上類上的對應的名稱類似於 專案下面的 模組名稱
如果貼在方法上面,就是訪問此方法的資源名稱

@Controller
@RequestMapping("/request")  //訪問時候必須加上,類似模組名稱
public class RequestController {
    @RequestMapping(value="/method1") //資源名稱 
    public void method1() {
    }
}

    訪問地址  : http://localhost:8080/springmvc/request/method1.do 

  7.2兩種限制

    SpringMVC支援對請求的限制.如果不滿足限制的條件,就不讓訪問執行方法。這樣做,大大提高了執行方法的安全性。
    主要的限制有兩種:方法限制(method),引數限制

      1.方法限制: 

       設定請求的method型別(get/post)。如果傳送過來的請求與方法設定的method不一樣,就不能訪問執行方法。

/*
     * @RequestMapping 請求對映註解
     * value : 請求的url地址對映,沒有其他引數時“value=”可以省略,有其他引數就不能省略
     * method :限定請求方式 GET /POST  預設沒有限制get/post都可以
     * params : 請求引數的限制:  必須有什麼引數,必須沒有什麼引數,引數的值必須是什麼
     */
    @RequestMapping(value="/login1",method=RequestMethod.POST)
    public ModelAndView login(HttpServletRequest request,HttpServletResponse reponse,HttpSession sesion ) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        System.out.println(username);
        System.out.println(password);
        return null;
        
    }
<%-- EL表示式獲取上下文路徑 : ${pageContext.request.contextPath} --%>
    <h4>登入頁面</h4>
    <form action="${pageContext.request.contextPath}/request/login1.do"
        method="get">
        賬號:<input name="username"><br>
            密碼:<input type="password" name="pwd"><br>
        <button type="submit">登入</button>
    </form>

不支援請求方法“GET”

    2.引數限制

      限制請求裡面必須包括哪些引數,或不包括哪些哪些,引數必須包括哪些值,不包括哪些值

      限制引數格式:

1.引數必須包括:params={"username","password"}
2.引數不能包括:params={"!userid"}
3引數值必須是指定的值:params={"username=zhangsan"})
4.引數值必須不是指定的值:params={"userid!=123"})

/*
     * @RequestMapping
     * 請求對映註解
     * value : 請求的url地址對映,沒有其他引數時“value=”可以省略,有其他引數就不能省略
     * method :限定請求方式 GET /POST  預設沒有限制get/post都可以
     * params : 請求引數的限制:  必須有什麼引數,必須沒有什麼引數,引數的值必須是什麼
     */
    @RequestMapping(value="/login2",params={"username","password"})
    public ModelAndView login(HttpServletRequest request,HttpServletResponse reponse,HttpSession sesion ) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        System.out.println(username);
        System.out.println(password);
        return null;
        
    }
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登入</title>
</head>
<body>
    <%-- EL表示式獲取上下文路徑 : ${pageContext.request.contextPath} --%>
    <h4>登入頁面</h4>
    <form action="${pageContext.request.contextPath}/request/login2.do"
        method="post">
        賬號:<input name="username"><br>
         密碼:<input type="password" name="pwd"><br>
        <button type="submit">登入</button>
    </form>
</body>
</html>

引數條件“username, password”不滿足,實際請求引數:username={admin}, pwd={123}

  7.3SpringMVC方法引數注入

    SpringMVC的方法預設可以注入 JavaWeb開發常用的資料共享物件,如:HttpServletRequest 、HttpServletResponse、HttpSession。獲取這些共享物件以後,就可以向之前的Servlet一樣,做任何資料共享以及頁面跳轉操作

8.資料繫結

  8.1.資料繫結是什麼

    SpringMVC的資料繫結就是將請求帶過來的表單資料繫結到執行方法的引數變數。
    實際開發中,SpringMVC作為表現層框架,肯定會接受前臺頁面傳遞過來的引數,SpringMVC提供了豐富的接受引數的方法

  8.2 接受引數的方法

    1. 使用HttpServletRequest物件(C)

  SpringMVC可以注入HttpServletRequest物件,直接使用getParameter()接受引數,但一般不會這種方法,還用這種方法接收引數的話使用SpringMVC的意義就不大,這種方法瞭解即可。

<!--  原始方式request.getParameter() -->
    <fieldset>
        <legend> 原始方式request.getParameter()</legend>
        <form action="${pageContext.request.contextPath}/request/method1.do"
            method="get">
            賬號: <input name="username"><br> 
                    年齡: <input name="age"><br>
            <button type="submit">提交</button>
        </form>
    </fieldset>
@RequestMapping("/method1")
    public ModelAndView method1(HttpServletRequest request) {
        String username = request.getParameter("username");
        String age = request.getParameter("age");
        
        System.out.println(username);
        System.out.println(age);
        return null;
    }

    2. 方法形參與請求引數同名

    在方法形參上,宣告和表單欄位名相同的引數名(可以進行自動同名匹配,然後注入)

<fieldset>
    <legend>方法形參與前臺引數同名</legend>
    <form action="${pageContext.request.contextPath}/request/method2.do"
        method="post">
        賬號: <input name="username"><br>
            年齡: <input name="age"><br>
        <button type="submit">提交</button>
    </form>
</fieldset>
@RequestMapping("/method2")
public ModelAndView method2(String username, Integer age) {
    System.out.println(username);
    System.out.println(age);
    return null;
}

    3.方法形參與請求引數不同名

    方法形參與表單欄位名引數名不同是,要使用 @RequestParam註解 繫結請求引數到方法引數

<fieldset>
        <legend>方法形參與前臺引數不同名</legend>
        <form action="${pageContext.request.contextPath}/request/method3.do"
            method="post">
            賬號: <input name="name"><br> 
                    年齡: <input name="age"><br>
            <button type="submit">提交</button>
        </form>
 </fieldset>
@RequestMapping("/method3")
    public ModelAndView method3(@RequestParam("name") String username, Integer age) {
        System.out.println(username);
        System.out.println(age);
        return null;
}

    4.接收陣列

<fieldset>
        <legend>接收陣列或集合</legend>
        <form action="${pageContext.request.contextPath}/request/method4.do"
            method="post">
            賬號: <input name="username"><br> 年齡: <input name="age"><br>
            愛好: <input type="checkbox" name="hobbys" value="java">java <input
                type="checkbox" name="hobbys" value="C">C <input
                type="checkbox" name="hobbys" value="C++">C++<br />
            <button type="submit">提交</button>
        </form>
 </fieldset>
@RequestMapping("/method4")
    public ModelAndView method4(String username, Integer age, String[] hobbys) {
        System.out.println(username);
        System.out.println(age);
        System.out.println(Arrays.toString(hobbys));
        return null;
 }

  如果後臺接收的屬性名與前臺不一致需要在陣列前面加上@RequestParam註解

    5.接收物件

  在實際開發中經常需要把接收的引數封裝到javaBaen物件中。springMVC提供了直接接收javaBaen物件的方式,直接把javaBaen物件作為方法的形參,從前臺接收到的引數就會自動對映注入到物件中,但必須保證javaBaen物件的屬性名與從前臺接收到的引數名一致,否則就無法對映

package com.gjs.pojo;

import java.util.Arrays;

public class User {
    private String username;
    private String password;
    private String email;
    private String phone;
    private String[] hobby;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String[] getHobby() {
        return hobby;
    }
    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
    public User() {
        super();
    }
    public User(String username, String password, String email, String phone, String[] hobby) {
        super();
        this.username = username;
        this.password = password;
        this.email = email;
        this.phone = phone;
        this.hobby = hobby;
    }
    @Override
    public String toString() {
        return "User [username=" + username + ", password=" + password + ", email=" + email + ", phone=" + phone
                + ", hobby=" + Arrays.toString(hobby) + "]";
    }
    
}
<fieldset>
        <legend>接受物件,表單引數名必須和後臺pojo物件對應的屬性名相同</legend>
        <form action="${pageContext.request.contextPath}/request/method5.do"
            method="get">
            賬號: <input name="username"><br> 
            密碼: <input    type="password" name="password"><br> 
            郵箱: <input    name="email"><br> 
            電話: <input name="phone"><br>
            愛好:<input type="checkbox" name="hobby" value="java">java
            <input type="checkbox" name="hobby" value="C">C 
            <input type="checkbox" name="hobby" value="C++">C++<br />
            <button type="submit">提交</button>
        </form>
</fieldset>
    //接受物件,直接在方法的引數上面注入pojo物件,pojo物件的屬性和表單的引數名必須一樣
    @RequestMapping("/method5")
    public ModelAndView method5(User user) {
        System.out.println(user);
        return null;
    }

    6.接受引數封裝成Map集合

  有的時候我們需要接收多個引數,但又不想使用物件接收,這時可以用Map集合接收。只需要把map集合作為方法引數注入然後在map引數前面加上@RequestParam 註解即可, SpringMVC在接受表單引數時候回自動建立一個map集合, 並且把表單的引數名作為map的key,引數值作為map的value。使用map集合可以接受任意個數的引數,但需要注意map只能接受單個值的引數,接收陣列型別的引數只會接收第一個元素

    <fieldset>
        <legend>接受引數封裝成Map集合</legend>
        <form action="${pageContext.request.contextPath}/request/method6.do" method="post">
            賬號: <input name="username"><br>
            密碼: <input name="password"><br>
            郵箱: <input name="email"><br>
            電話: <input name="phone"><br>
            愛好:<input type="checkbox" name="hobby" value="java">java
            <input type="checkbox" name="hobby" value="C">C 
            <input type="checkbox" name="hobby" value="C++">C++<br />
            <button type="submit">提交</button>
        </form>
    </fieldset>
    @RequestMapping("/method6")
    public ModelAndView method6(@RequestParam Map<String, Object> paramsMap) {
        System.out.println(paramsMap);
        return null;
    }

  7.RESTFUL風格支援

  RESTFUL介紹:

  REST(英文:Representational State Transfer,簡稱REST)描述了一個架構樣式的網路系統,比如 web 應用程式。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規範的主要編寫者之一。在目前主流的三種Web服務互動方案中,REST相比於SOAP(Simple Object Access protocol,簡單物件訪問協議)以及XML-RPC更加簡單明瞭,REST都傾向於用更加簡單輕量的方法設計和實現。值得注意的是REST並沒有一個明確的標準,而更像是一種設計的風格。
  RESTful一種軟體架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和伺服器互動類的軟體。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。

  引數傳遞方法 GET

例如:根據商品id查詢對應的商品資訊(京東網站)
如果按照我們web開發應該是將商品id通過get方法跟在地址後面
普通方式 https://item.jd.com?product_id=100000287117 京東不支援
但是京東不是使用的此種方式,使用的 RESTFUL風格
RESTful風格 : https://item.jd.com/100000287117.html

  示例程式碼:

    @PathVariable用於將請求URL中的模板變數(動態引數)對映到功能處理方法的引數上。

// RESTful風格
@RequestMapping(value = "/method7/{product_id}.html")
public ModelAndView method7(@PathVariable("product_id") Integer product_id) {
System.out.println(product_id);//1231323123
return null;
}

  訪問地址:localhost:8080/springmvc/request/method7/1231323123.html
     注:.html 的url會被Tomcat當做靜態資源處理了,所以配置springmvc的前端控制器時對映路徑要陪 / ,然後再配置開啟對靜態資源的訪問 <mvc:default-servlet-handler/>

  使用RESTful優勢

1.可以讓頁面偽靜態,頁面訪問感覺像在訪問靜態html頁面,實際上訪問時動態頁面(偽靜態)
2.方便搜尋引擎的SEO優化(html頁面在搜尋引擎搜尋結果比較靠前)

 

9.請求中文亂碼問題

  SpringMVC預設接受的引數是ISO-8859-1編碼引數,單位元組,不支援中文。Spring提供了一個過濾器 org.springframework.web.filter.CharacterEncodingFilter 可以讓開發者自定義請求引數的字元編碼。
  在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>

  如果是get方式還需要修改Tomcat的配置:修改tomcat/conf/server.xml,在<Connector> 標籤中新增屬性useBodyEncodingForURI="true"(Tomcat 8之後版本可不改,預設就是utf-8)
  useBodyEncodingForURI="true":是否設定用request的字符集對URL提交的資料和表單中GET方式提交的資料進行重新編碼

10.響應傳值方式

  10.1使用原始servlet的請求和響應物件進行頁面跳轉和資料共享

    @RequestMapping("method1")
    public void method1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //資料共享
        request.setAttribute("username", "張三");
        
        //請求轉發
        //request.getRequestDispatcher("/WEB-INF/response.jsp").forward(request, response);
        
        //url重定向(可以跨域訪問)
        response.sendRedirect("http://www.baidu.com");
    }

  10.2返回ModelAndView型別進行頁面跳轉和資料共享

    ModelAndView :模型和檢視。Spring提供此物件可以集中管理共享資料操作和設定跳轉檢視操作。但 ModelAndView 只能使用請求轉發。

    @RequestMapping("method2")
    public ModelAndView method2() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("username", "李四");    //資料共享
        mv.setViewName("/WEB-INF/response.jsp");//設定檢視地址
        return mv;
    }

  10.3 通過Model方式-設定共享資料

    直接將需要共享的資料封裝到Model物件,方法返回值(String字串)為需要跳轉的地址。 預設使用請求轉發

    @RequestMapping("method3")
    public String method3(Model m) {
        m.addAttribute("username", "王五");    //資料共享
        return "/WEB-INF/response.jsp";        //返回檢視地址
    }

  10.4 配置檢視解析器

  檢視解析器的配置

<!-- 配置SpringMVC的檢視解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        
        <!-- 配置檢視字首 -->
        <property name="prefix" value="/WEB-INF/"/>
        
        <!-- 配置檢視字尾 -->
        <property name="suffix" value=".jsp"/>
    
    </bean>

  配置檢視解析器後的程式碼

    Controller方法返回字串表示邏輯檢視名,通過檢視解析器解析為物理檢視地址。
    此時預設的物理檢視地址為:檢視字首+邏輯檢視名稱+檢視字尾

    @RequestMapping("method2")
    public ModelAndView method2() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("username", "李四");        //資料共享
        //配置檢視解析器之前
        //mv.setViewName("/WEB-INF/response.jsp");//設定檢視地址
        //配置檢視解析器之後:配置一個邏輯檢視名稱
        mv.setViewName("response");
        return mv;
    }
    
    
    @RequestMapping("method3")
    public String method3(Model m) {
        m.addAttribute("username", "王五");    //資料共享
        //配置檢視解析器之前
        //return "/WEB-INF/response.jsp";        //返回檢視地址
        //配置檢視解析器之後:返回一個邏輯檢視名稱
        return "response";
    }

  10.5 自定義請求轉發和重定向跳轉的頁面

如果直接使用檢視解析器的配置開發,那麼必須保證檢視解析器字首目錄下面有對應的頁面檔案才能跳轉,否則報錯。
預設頁面跳轉也只能使用請求轉發跳轉,不能使用重定向 需要解決問題: 除了使用檢視解析器對應規則的開發,使用者還得自定義跳轉方式,和自定義跳轉頁面 方案:
使用檢視解析器的 請求轉發和重定向配置,可以打破預設的規則
spring原始碼:

public static final String REDIRECT_URL_PREFIX = "redirect:"; 
public static final String FORWARD_URL_PREFIX = "forward:";

重定向:redirect:
請求轉發:forward:

示例程式碼:

    // 自定義請求轉發頁面跳轉的地址 : forward: 跳轉的地址
    @RequestMapping("method4")
    public String method4(Model m) {
        m.addAttribute("username", "趙六");
        return "forward:/WEB-INF/response.jsp";    
    }
    
    // 自定義重定向頁面跳轉的地址 redirect: 跳轉的地址
    @RequestMapping("method5")
    public String method5(Model m) {
        return "redirect:http://www.baidu.com";    
    }

  10.6 返回物件進行頁面跳轉和資料共享

  返回物件預設使用就是請求轉發,跳轉的頁面規則 :檢視解析器字首 + 模組名+@RequestMapping的值 + 字尾。返回的物件即共享的資料,共享的名稱預設為物件型別的首字母小寫。可以使用@ModelAttribute("設定共享模型的屬性名稱")

    @RequestMapping("method6")
    @ModelAttribute("userKey") //設定共享模型的屬性名稱
    public User method6() {
        User user = new User("馬七","123", "10000@qq.com", "1", null);
        return user;
    }

  

  10.7 轉換JSON資料

在web開發中,前臺頁面經常會傳送ajax請求從後臺請求資料,ajax請求給前臺的資料一般都是json 資料。
SpringMVC支援自動將物件轉換JSON格式的資料響應給客戶端
SpringMVC預設使用的是 jackson 作為物件轉json的工具

先匯入jackson的jar包
下載地址:https://mvnrepository.com/search?q=jackson

      

    使用@ResponseBody註解方法的返回值會以字串的形式返回,springMVC會自己呼叫jackson 把物件轉換成json字串

@RequestMapping("/getObjectByJson")
    @ResponseBody //將方法的返回值以字串的形式返回
    public User getObjectByJson() {
        User user = new User();
        user.setUsername("小明");
        user.setPassword("123");
        user.setPhone("135xxxx");
        user.setEmail("xiaoming@qq.com");
        return user;
    } 

    @RequestMapping("/getListtByJson")
    @ResponseBody 
    public List<User> getListByJson() {
        
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setUsername("小明 : "+i);
            user.setPassword("123 :"+i);
            user.setPhone("135xxxx :"+i);
            user.setEmail( "xiaoming@qq.com");
            users.add(user);
        }
        
        return users;
    }

11. 檔案上傳

  在web開發中一般都會有檔案上傳的操作
  一般在JavaWeb開發中檔案上傳使用的 Apache組織的Commons FileUpload元件
  SpringMVC中使用 MultipartFile file物件接受上傳檔案,必須保證 後臺引數的名稱和表單提交的檔案的名稱一致
  檔案上傳必須條件
    1.表單必須post
    2.表單必須有 file 檔案域
    3.表單的 enctype="multipart/form-data"

  11.1 單個檔案上傳    

    步驟:
    1.匯入相關jar包

    

 

     

    2.在springmvc.xml中配置上傳解析器
    property的value用來設定上傳檔案的最大尺寸,其他配置為固定配置

<!-- 配置檔案上傳解析器:bean的名字是固定的,底層使用的名稱注入 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設定上傳檔案的最大尺寸為1MB -->
<property name="maxUploadSize" value="#{1024 * 1024}"></property>
</bean>

    3.jsp程式碼

<fieldset>
        <legend>單個檔案上傳</legend>
        <form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
            姓名: <input name="username"><br>
            頭像: <input type="file" name="headImg"><br>
            <button type="submit">提交</button>
        </form>
    </fieldset>
<fieldset>

    4.後臺程式碼
    SpringMVC中使用 MultipartFile物件接受上傳檔案,必須保證 後臺方法MultipartFile 引數的名稱和表單提交的檔案的名稱一致

 

     //SpringMVC中使用  MultipartFile物件接受上傳檔案,必須保證 後臺引數的名稱和表單提交的檔案的名稱一致
        @RequestMapping("/upload")
        public String singleUpload(MultipartFile headImg,@RequestParam("username")String username) throws IOException {
             
            System.out.println(headImg.getName());//獲取上傳檔案的表單引數名稱 
            
                    System.out.println(headImg.getContentType());//MIME型別:如html的MIME型別為text/html,用於確定檔案屬於哪一型別的
            System.out.println(headImg.getSize());//檔案大小
            System.out.println(headImg.getOriginalFilename());//獲取上傳檔案的完整名稱
            //獲取上傳檔案對應的輸入流
            //InputStream in = headImg.getInputStream();
            
            //建立一個磁碟目錄(資料夾)用於儲存檔案
            File destFile= new File("D:/upload");
            if(!destFile.exists()) {    //判斷資料夾是否存在
                destFile.mkdir();
            }
            //使用uuid作為檔案隨機名稱
            String fileName = UUID.randomUUID().toString().replaceAll("-", "");
            //使用FileNameUtils獲取上傳檔名的字尾
            String extension = FilenameUtils.getExtension(headImg.getOriginalFilename());// jpg , png 等等
            //建立新的檔名稱
            String newFileName = fileName + "."+extension;
             
            //建立要儲存檔案的File物件
            File file = new File(destFile, newFileName);
            //儲存檔案到本地磁碟
            headImg.transferTo(file);
            
            return "redirect:/upload.jsp";
        }

  11.2 多檔案上傳

  <fieldset>
        <legend>多個檔案上傳</legend>
        <form name="files" action="${pageContext.request.contextPath}/uploads.do" method="post" enctype="multipart/form-data">
            檔案1: <input type="file" name="headImgs"><br>
            檔案2: <input type="file" name="headImgs"><br>
            檔案3: <input type="file" name="headImgs"><br>
            <button type="submit">提交</button>
        </form>
    </fieldset>
        @RequestMapping("/uploads")
        public String singleUploads(MultipartFile[] headImgs) throws  IOException {
            //建立一個磁碟目錄(資料夾)用於儲存檔案
            File destFile= new File("D:/upload");
            if(!destFile.exists()) {    //判斷資料夾是否存在
                destFile.mkdir();
            }
            
            if(headImgs!=null) {
                for (MultipartFile headImg : headImgs) {
                    //使用uuid作為檔案隨機名稱
                    String fileName = UUID.randomUUID().toString().replaceAll("-", "");
                    //使用FileNameUtils獲取上傳檔名的字尾
                    String extension = FilenameUtils.getExtension(headImg.getOriginalFilename());// jpg , png 等等
                    //拼接新的檔名稱
                    String newFileName = fileName + "."+extension;
                    
                    //建立要儲存檔案的File物件
                    File file = new File(destFile, newFileName);
                    
                    //儲存檔案到本地磁碟
                    headImg.transferTo(file);
                    
                    
                }
            }
            return "redirect:/upload.jsp";
        
        }

12.檔案下載

  檔案下載,SpringMVC並沒有做過多的封裝,還是使用原來的下載方式
  下載檔案思路:
    1. 接受需要下載檔名稱,根據檔名稱,找到磁碟對應的檔案,讀取到記憶體中形成一個輸入流
    2. 將輸入流通過響應物件(HttpServletResponse)響應給瀏覽器(下載)

  @RequestMapping("/download")
    public void download(String fileName,HttpServletResponse response) throws IOException {
            
        //通過接受到的檔名從磁碟中讀取對應檔案
        FileInputStream input = new FileInputStream("D:/"+fileName);
            
        //獲取響應物件的輸出流
        ServletOutputStream output = response.getOutputStream();
            
        //將檔名的編碼轉為ISO-8859-1編碼(響應頭編碼),否則傳給瀏覽器的檔名中文會亂碼
        byte[] bytes = fileName.getBytes("utf-8");
        fileName= new String(bytes, "ISO-8859-1");
            
        //響應內容以附件的形式響應給瀏覽器,並設定檔名
        response.setHeader("Content-Disposition", "attachment;filename="+fileName);
            
        //響應檔案給瀏覽器
        IOUtils.copy(input, output);
            
    }

13.SpringMVC的攔截器

  攔截器 (Interceptor):Spring MVC 的攔截器類似於Servlet 開發中的過濾器Filter,用於對Controller進行預處理和後處理。攔截器只會攔截控制器請求,不會攔截jsp頁面。
  使用SpringMVC攔截器步驟:
    1.定義攔截器類,實現介面 org.springframework.web.servlet.HandlerInterceptor
    2.在applicationContext.xml中配置攔截器

  13.1 定義攔截器

    攔截器方法的執行時機:
      1.preHandle:控制器方法執行之前執行,返回結果為true表示放行,如果返回為false,表示攔截(可以做許可權攔截,登入檢查攔截).
      2.postHandle:控制器方法執行後,檢視渲染之前執行(可以加入統一的響應資訊).
      3.afterCompletion:檢視渲染之後執行(處理Controller異常資訊,記錄操作日誌,清理資源等)

/**
 * 驗證登入攔截器
 */
public class CheckLoginInterceptor implements HandlerInterceptor{

    /*
     * 控制器方法執行之前執行
     * 返回結果為true表示放行,如果返回為false,表示攔截(可以做許可權攔截,登入檢查攔截).
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //獲取session中的user物件
        User user = (User)request.getSession().getAttribute("user");
        
        if(user==null) {    //user==null表示為登入
            //重定向跳轉到登入頁面 login.jsp
            response.sendRedirect(request.getContextPath()+"/login.jsp");
            return false;    //攔截請求
        }
        return true;        //放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
    }
}

  13.2 配置攔截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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.xsd
        ">
    <!-- 開啟springMVC註解驅動 -->
    <mvc:annotation-driven/>
    
    <!-- 配置攔截器 -->
    <mvc:interceptors>
        <!-- 配置登入檢查攔截器 -->
        <mvc:interceptor>
            <!-- 攔截的地址(只會攔截 控制器請求,不會攔截jsp頁面)
                /*:只能攔截一級, 如:login.do
                /**:可以攔截多級,如:a/b/c/d/login.do
             -->
            <mvc:mapping path="/**"/>
            
            <!-- 排除攔截的地址,多個地址使用逗號隔開 -->
            <mvc:exclude-mapping path="/user/login.do"/>
            
            <!-- 自定義的攔截器 -->
            <bean class="com.gjs.ssm.interceptor.CheckLoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

14. 使用poi元件匯出excel檔案

  14.1 匯入jar包

    

  14.2 示例程式碼

// 匯出使用者資訊
@RequestMapping("/exprot")
public void export(HttpServletResponse response) {
//建立POI的資料物件 工作集
HSSFWorkbook book = new HSSFWorkbook();
//建立sheet
HSSFSheet sheet = book.createSheet();
//建立行(索引從0開始,表示第一行)
HSSFRow titleRow = sheet.createRow(0);
//使用行建立列並設定資料(索引從0開始,表示第一列)
titleRow.createCell(0).setCellValue("編號");
titleRow.createCell(1).setCellValue("姓名");
titleRow.createCell(2).setCellValue("郵箱");
titleRow.createCell(3).setCellValue("電話");
List<User> users = service.list();
//迴圈集合,每一條資料建立一行,並把對應的值設定列中
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
//建立行
HSSFRow row = sheet.createRow(i+1);
//建立列,並將學生資訊設定到對應列中
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getName());
row.createCell(2).setCellValue(user.getEmail());
row.createCell(3).setCellValue(user.getPhone());
}
try {
//設定響應頭,響應的內容是為附件形式
response.addHeader("Content-Disposition",
"attachment;filename=" + new String("學生資訊.xlsx".getBytes(), "ISO-8859-1"));
book.write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}

15. SpringMVC 控制器 Controller的生命週期

Spring 容器建立的物件預設是單例物件。可以通過@Scope註解來設定
SpringMVC物件 Controller的物件的建立有三種情況:
  預設:單例,僅在伺服器啟動時建立,並一直存在
  Request : 在使用者的一次請求中生效(使用者每次請求都會建立Controller物件)多例
  Session : Controller物件在一次會話中建立一個物件

如果控制器中有成員變數 設定或者賦值操作,必須使用 request 返回

   

 

相關文章