SpringMVC(二):SpringMVC的進階

第1章 響應數據和結果視圖

環境搭建

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>top.onefine</groupId>
    <artifactId>springmvc_day02_01_response</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>springmvc_day02_01_response Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <!-- Spring版本鎖定-->
        <spring.version>5.2.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>springmvc_day02_01_response</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

src/main/webapp/WEB-INF/web.xml:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!-- 配置前端控制器——SpringMVC的核心控制器 -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <!-- 配置Servlet的初始化參數,加載SpringMVC配置文件,創建Spring容器-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>  <!-- 配置使得啓動服務器時就創建servlet對象,而後就可以接着加載SpringMVC配置文件 -->
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern><!-- 攔截所有 -->
  </servlet-mapping>

  <!-- 配置解決中文亂碼的過濾器 -->
  <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>
</web-app>

src/main/resources/springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描,配置spring創建容器時要掃描的包 -->
    <context:component-scan base-package="top.onefine" />

    <!-- 配置視圖解析器對象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />  <!-- prefix表示文件所在的位置 -->
        <property name="suffix" value=".jsp" />  <!-- suffix表示文件的後綴名 -->
    </bean>

    <!-- 配置Spring開啓註解MVC的支持 -->
    <mvc:annotation-driven />

</beans>

1.1返回值分類

1.1.1 字符串

controller 方法返回字符串可以指定邏輯視圖名,通過視圖解析器解析爲物理視圖地址。

指定邏輯視圖名,經過視圖解析器解析爲 jsp 物理路徑: /WEB-INF/pages/success.jsp

<%--
  Created by IntelliJ IDEA.
  User: ONEFINE
  Date: 2020/4/7
  Time: 21:01
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>成功頁面</title>
</head>
<body>
<h2>SpringMVC入門成功!</h2>

${ user } <br/>
姓名:${ user.username } <br/>
年齡:${ user.age }

</body>
</html>

src/main/java/top/onefine/domain/User.java:

package top.onefine.domain;

import lombok.*;

import java.io.Serializable;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    private String username;
    private String password;
    private Integer age;
}

src/main/webapp/response.jsp:

<%--
  Created by IntelliJ IDEA.
  User: ONEFINE
  Date: 2020/4/9
  Time: 19:41
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>response</title>
</head>
<body>
<a href="user/testString">testString</a>
</body>
</html>

src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import top.onefine.domain.User;

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/testString")
    public String testString(Model model) {
        System.out.println("testString方法執行了...");
        // 模擬從數據庫中查詢出User對象
        User user = new User("onefine", "963", 18);
        // 將user對象存入model中(request域中)
        model.addAttribute("user", user);
        // 跳轉到xxx頁面
        return "success";  // 返回字符串
    }
}

在這裏插入圖片描述

1.1.2 void

默認情況:

@RequestMapping("/testVoid")
public void testVoid() {
    System.out.println("testVoid方法執行了...");
}

jsp:<a href="user/testVoid">testVoid</a>

在這裏插入圖片描述

Servlet 原始 API 可以作爲控制器中方法的參數:

@RequestMapping("/testReturnVoid")
public void testReturnVoid(HttpServletRequest request,HttpServletResponse response) throws Exception {
}

在controller 方法形參上可以定義 request 和 response,使用 request 或 response 指定響應結果:

1、使用 request 轉向頁面,如下:

request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);

2、也可以通過 response 頁面重定向:

response.sendRedirect("testRetrunString")

3、也可以通過 response 指定響應結果, 例如響應 json 數據:

response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json 串");

例子:

	/**
     * 注意:
     *      請求轉發是一次請求,不需編寫項目的名稱
     *      重定向是兩次請求,要寫項目名稱
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("testVoid方法執行了...");
        // 請求轉發  手動調用轉發的方法不會再調 視圖解析器 了,需要自己提供完整目錄
//        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);  // 請求轉發不用寫項目名稱
        // 重定向  不能直接請求/WEB-INF目錄中的頁面
//        response.sendRedirect(request.getContextPath() + "/index.jsp");
        // 直接給瀏覽器進行響應
            // 設置中文亂碼
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("hello 中文");
    }

注意:重定向是兩次請求,

在這裏插入圖片描述

1.1.3 ModelAndView

ModelAndView 是 SpringMVC 爲我們提供的一個對象,該對象可以用來調整具體的JSP視圖,用作控制器方法的返回值。

注:返回字符串的底層也是採用此方式

該對象中有兩個方法:

在這裏插入圖片描述
在這裏插入圖片描述

示例:

	@RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        System.out.println("testModelAndView方法執行了...");
        // 創建ModelAndView對象
        ModelAndView mv = new ModelAndView();
        // 模擬從數據庫中查詢出User對象
        User user = new User("onefine", "963", 18);
        // 把user對象存儲到mv對象中,也會吧user對象存入到request域中
        mv.addObject("user", user);

        // 跳轉到指定頁面
        mv.setViewName("success");

        // 注:返回字符串的底層也是採用此方式

        return mv;
    }

1.2轉發和重定向

1.2.1 forward 轉發

controller 方法在提供了 String 類型的返回值之後,默認就是請求轉發。我們也可以寫成:

	@RequestMapping(value = "/testForward")
    public String testForward() {
        System.out.println("testForward方法執行了");
        // 請求的轉發
        // "forward:轉發的JSP路徑",不走視圖解析器了,所以需要編寫完整的路徑
        return "forward:/WEB-INF/pages/success.jsp";  // 關鍵字forward
    }

需要注意的是,如果用了 formward: 則路徑必須寫成實際視圖 url,不能寫邏輯視圖。

它相當於request.getRequestDispatcher("url").forward(request,response)。使用請求轉發,既可以轉發到 jsp,也可以轉發到其他的控制器方法。

1.2.2 Redirect 重定向

contrller 方法提供了一個 String 類型返回值之後, 它需要在返回值裏使用:redirect:

	@RequestMapping(value = "/testRedirect")
    public String testRedirect() {
        System.out.println("testRedirect方法執行了");
        // 重定向,還是不能訪問/WEB-INF/的頁面,但是不用加項目名了
        return "redirect:/index.jsp";  // 關鍵字redirect
    }

它相當於“response.sendRedirect(url)” 。需要注意的是,如果是重定向到 jsp 頁面,則 jsp 頁面不能寫在 WEB-INF 目錄中,否則無法找到。

1.3ResponseBody響應 json 數據

1.3.1 使用說明

作用:該註解用於將 Controller 的方法返回的對象,通過 HttpMessageConverter 接口轉換爲指定格式的數據如: json,xml 等,通過 Response 響應給客戶端。

1.3.2 使用示例

需求:
使用@ResponseBody 註解實現將 controller 方法返回對象轉換爲 json 響應給客戶端。

前置知識點:

Springmvc 默認用 MappingJacksonHttpMessageConverter 對 json 數據進行轉換,需要加入jackson 的包:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.10.3</version>
 </dependency>

 <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
 <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.10.3</version>
 </dependency>

 <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
 <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.10.3</version>
 </dependency>

jsp 中的代碼:

<%--
  Created by IntelliJ IDEA.
  User: ONEFINE
  Date: 2020/4/9
  Time: 19:41
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>response</title>
    <script src="js/jquery-3.4.1.js"></script>
    <script>
        // 頁面加載,綁定單擊事件
        $(function () {
            $("#btn").click(function () {
                $.ajax({
                    // 編寫json格式,設置屬性和值
                    url: "user/testAjax",
                    contentType: "application/json;charset=UTF-8",
                    data: '{"username": "one fine", "password": "123", "age": 18}',
                    dataType: "json",
                    type: "post",
                    // 處理成功後的回調函數
                    success: function (data) {
                        alert(data.username + ", " + data.password + ", " + data.age);
                    }
                });
            });
        })
    </script>
</head>
<body>
<a href="user/testString">testString</a> <br/>
<a href="user/testVoid">testVoid</a> <br/>

<a href="user/testModelAndView">testModelAndView</a> <br/>

<a href="user/testForward">testForward</a> <br/>

<a href="user/testRedirect">testRedirect</a> <br/>

<button id="btn">發送ajax請求</button>

</body>
</html>

配置靜態資源不攔截: src/main/resources/springmvc.xml:

DispatcherServlet會攔截到所有的資源,導致一個問題就是靜態資源(img、css、js)也會被攔截到,從而不能被使用。解決問題就是需要配置靜態資源不進行攔截,在springmvc.xml配置文件添加:mvc:resources標籤,配置不過濾:

  • location元素表示webapp目錄下的包下的所有文件
  • mapping元素表示以/static開頭的所有請求路徑,如/static/a 或者/static/a/b
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描,配置spring創建容器時要掃描的包 -->
    <context:component-scan base-package="top.onefine" />

    <!-- 配置視圖解析器對象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />  <!-- prefix表示文件所在的位置 -->
        <property name="suffix" value=".jsp" />  <!-- suffix表示文件的後綴名 -->
    </bean>

    <!-- 配置前端控制器:靜態資源不攔截(過濾) -->
    <mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
    <mvc:resources mapping="/css/**" location="/WEB-INF/css/" />
    <mvc:resources mapping="/images/**" location="/WEB-INF/images/" />

    <!-- 配置Spring開啓註解MVC的支持 -->
    <mvc:annotation-driven />

</beans>

控制器中的完整代碼:

  • 使用@RequestBody獲取請求體數據,@RequestBody註解也可把json的字符串直接轉換成JavaBean的對象
  • 使用@ResponseBody註解把JavaBean對象轉換成json字符串,直接響應
  • json字符串和JavaBean對象互相轉換的過程中,需要使用jackson的jar包
package top.onefine.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import top.onefine.domain.User;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/testString")
    public String testString(Model model) {
        System.out.println("testString方法執行了...");
        // 模擬從數據庫中查詢出User對象
        User user = new User("onefine", "963", 18);
        // 將user對象存入model中(request域中)
        model.addAttribute("user", user);
        return "success";  // 返回字符串
    }

    /**
     * 注意:
     *      請求轉發是一次請求,不需編寫項目的名稱
     *      重定向是兩次請求,要寫項目名稱
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("testVoid方法執行了...");
        // 請求轉發  手動調用轉發的方法不會再調 視圖解析器 了,需要自己提供完整目錄
//        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response); // 請求轉發不用寫項目名稱
        // 重定向  不能直接請求/WEB-INF目錄中的頁面
//        response.sendRedirect(request.getContextPath() + "/index.jsp");
        // 直接給瀏覽器進行響應
        // 設置中文亂碼
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("hello 中文");
    }

    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        System.out.println("testModelAndView方法執行了...");
        // 創建ModelAndView對象
        ModelAndView mv = new ModelAndView();
        // 模擬從數據庫中查詢出User對象
        User user = new User("onefine", "963", 18);
        // 把user對象存儲到mv對象中,也會吧user對象存入到request域中
        mv.addObject("user", user);

        // 跳轉到指定頁面
        mv.setViewName("success");

        // 注:返回字符串的底層也是採用此方式

        return mv;
    }

    @RequestMapping(value = "/testForward")
    public String testForward() {
        System.out.println("testForward方法執行了");
        // 請求的轉發
        return "forward:/WEB-INF/pages/success.jsp";  // 關鍵字forward
    }

    @RequestMapping(value = "/testRedirect")
    public String testRedirect() {
        System.out.println("testRedirect方法執行了");
        // 重定向,還是不能訪問/WEB-INF/的頁面,但是不用加項目名了
        return "redirect:/index.jsp";  // 關鍵字redirect
    }

    @RequestMapping("/testAjax")
    public @ResponseBody User testAjax(@RequestBody User user) {
        // 客戶端發送ajax的請求,傳的是json字符串,後端已經將json字符串封裝到user對象中了
        System.out.println(user);  // 獲得請求體
        // 做響應,模擬查詢數據庫
        user.setUsername("one");
        user.setAge(19);
        return user;  // 由@ResponseBody自動將user對象轉爲json對象
    }

}

第2章 SpringMVC 實現文件上傳

2.1文件上傳的回顧

2.1.1 文件上傳的必要前提

  • form 表單的 enctype 取值必須是: multipart/form-data,(默認值是:application/x-www-form-urlencoded)。enctype:是表單請求正文的類型
  • method 屬性取值必須是 Post
  • 提供一個文件選擇域<input type=”file” />

2.1.2 文件上傳的原理分析

當 form 表單的 enctype 取值不是默認值後, request.getParameter()將失效。

enctype=”application/x-www-form-urlencoded”時, form 表單的正文內容是:key=value&key=value&key=value

當 form 表單的 enctype 取值爲 Mutilpart/form-data 時,請求正文內容就變成:

每一部分都是 MIME 類型描述的正文
-----------------------------7de1a433602ac 				# 分界符
Content-Disposition: form-data; name="userName" 		# 協議頭
aaa 													# 協議的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 								# 協議的類型( MIME 類型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--

2.1.3 藉助第三方組件實現文件上傳

使用 Commons-fileupload 組件實現文件上傳,需要導入該組件相應的支撐 jar 包: Commons-fileuploadcommons-io

commons-io 不屬於文件上傳組件的開發 jar 文件,但Commons-fileupload 組件從 1.1 版本開始,它工作時需要 commons-io 包的支持。

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>


<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

2.2 傳統方式的文件上傳

2.2.1 說明

傳統方式的文件上傳, 指的是我們上傳的文件和訪問的應用存在於同一臺服務器上。並且上傳完成之後,瀏覽器可能跳轉。

2.2.2 非SpringMVC實現步驟

src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
import java.util.UUID;

@Controller
@RequestMapping("/user")
public class UserController {
    // 非SpringMVC方式文件上傳
    @RequestMapping("/fileUpload1")
    public String fileUpload1(HttpServletRequest request) throws Exception {
        System.out.println("文件上傳...");
        // 使用fileupload組件完成文件上傳
        // 指定上傳的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");

        // 判斷該文件路徑是否存在
        File file = new File(path);
        if (!file.exists())
            if (!file.mkdir()) // 創建該文件夾
                throw new Exception("文件夾創建失敗");

        // 解析request對象,獲取上傳的文件項
        DiskFileItemFactory factory = new DiskFileItemFactory();  // 磁盤文件項工廠
        ServletFileUpload upload =  new ServletFileUpload(factory);  // 創建文件項
        List<FileItem> items = upload.parseRequest(request);// 解析request
        // 遍歷進行處理
        for (FileItem item : items) {
            if (item.isFormField()) {
                // 普通表單項
                System.out.println("普通表單項");
            } else {
                // 上傳文件項
                String filename = item.getName();  // 獲取上傳文件名稱
                    // 將文件的名稱設置唯一值,防止文件重複:加上隨機數
                String uuid = UUID.randomUUID().toString().replace("-", "");
                filename = uuid + "_" + filename;
                item.write(new File(path, filename));  // 完成文件上傳
                item.delete();  // 刪除臨時文件
                System.out.println("上傳文件項");
            }
        }

        return "success";
    }
}

src/main/webapp/index.jsp:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>文件上傳</title>
</head>
<body>
<h2>傳統方式實現文件上傳</h2>

<form action="user/fileUpload1" method="post" enctype="multipart/form-data">
    名稱:<input type="text" name="picname"/><br/>
    文件:<input type="file" name="upload" /><br/>
    <input type="submit" value="上傳">
</form>


</body>
</html>

src/main/webapp/WEB-INF/pages/success.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>成功頁面</title>
</head>
<body>
<h2>文件上傳成功!</h2>

</body>
</html>

在這裏插入圖片描述
在這裏插入圖片描述

2.2.3 SpringMVC 的實現步驟

在這裏插入圖片描述
src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
import java.util.UUID;

@Controller
@RequestMapping("/user")
public class UserController {
    // 傳統方式文件上傳
    // springMVC實現文件上傳
    @RequestMapping("/fileUpload2")
    public String fileUpload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        // 注意:MultipartFile的名稱upload必須和前端 <input type="file" name="upload" /> 的name名稱一樣

        // 指定上傳的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判斷該文件路徑是否存在
        File file = new File(path);
        if (!file.exists())
            if (!file.mkdir()) // 創建該文件夾
                throw new Exception("文件夾創建失敗");

        String filename = upload.getOriginalFilename();  // 獲取上傳文件原始名稱
        String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        filename = uuid + "_" + filename;
        // 完成文件上傳
        upload.transferTo(new File(path, filename));
        return "success";
    }
}

配置文件上傳解析器對象src/main/resources/springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描,配置spring創建容器時要掃描的包 -->
    <context:component-scan base-package="top.onefine" />

    <!-- 配置視圖解析器對象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />  <!-- prefix表示文件所在的位置 -->
        <property name="suffix" value=".jsp" />  <!-- suffix表示文件的後綴名 -->
    </bean>

    <!-- 配置前端控制器:靜態資源不攔截(過濾) -->
    <mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
    <mvc:resources mapping="/css/**" location="/WEB-INF/css/" />
    <mvc:resources mapping="/images/**" location="/WEB-INF/images/" />

    <!-- 配置文件上傳解析器對象 -->
    <!-- 注意: id 的值是固定的 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 設置上傳文件的最大尺寸爲 10MB = 10 * 1024 * 1024 byte -->
        <property name="maxUploadSize" value="10485760" />
    </bean>

    <!-- 配置Spring開啓註解MVC的支持 -->
    <mvc:annotation-driven />

</beans>

注意: 文件上傳的解析器 id 是固定的,不能起別的名稱,否則無法實現請求參數的綁定。(不光是文件,其他字段也將無法綁定)

jsp文件:

<form action="user/fileUpload2" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="upload" /><br/>
    <input type="submit" value="上傳">
</form>
<!--
<form action="/fileUpload" method="post" enctype="multipart/form-data">
名稱: <input type="text" name="picname"/><br/>
圖片: <input type="file" name="uploadFile"/><br/>
<input type="submit" value="上傳"/>
</form>
-->

其他參考:

@Controller("fileUploadController")
public class FileUploadController {
/**
* 文件上傳
*/
@RequestMapping("/fileUpload")
public String testResponseJson(String picname,MultipartFile
	uploadFile,HttpServletRequest request) throws Exception{
	//定義文件名
	String fileName = "";
	//1.獲取原始文件名
	String uploadFileName = uploadFile.getOriginalFilename();
	//2.截取文件擴展名
	String extendName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1, uploadFileName.length());
	//3.把文件加上隨機數,防止文件重複
	String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
	//4.判斷是否輸入了文件名
	if(!StringUtils.isEmpty(picname)) {
		fileName = uuid+"_"+picname+"."+extendName;
	} else {
		fileName = uuid+"_"+uploadFileName;
	}
	System.out.println(fileName);
	//2.獲取文件路徑
	ServletContext context = request.getServletContext();
	String basePath = context.getRealPath("/uploads");
	//3.解決同一文件夾中文件過多問題
	String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
	//4.判斷路徑是否存在
	File file = new File(basePath+"/"+datePath);
	if(!file.exists()) {
		file.mkdirs();
	}
	//5.使用 MulitpartFile 接口中方法,把上傳的文件寫到指定位置
	uploadFile.transferTo(new File(file,fileName));
	return "success";
	}
}

2.3 springmvc 跨服務器方式的文件上傳

2.3.1 分服務器的目的

在實際開發中,我們會有很多處理不同功能的服務器。例如:

  • 應用服務器:負責部署我們的應用
  • 數據庫服務器:運行我們的數據庫
  • 緩存和消息服務器:負責處理大併發訪問的緩存和消息
  • 文件服務器:負責存儲用戶上傳文件的服務器。

(注意:此處說的不是服務器集羣)

分服務器處理的目的是讓服務器各司其職,從而提高我們項目的運行效率。

在這裏插入圖片描述

在這裏插入圖片描述

文件服務器環境搭建

src/main/webapp/WEB-INF/web.xml:

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <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>
        <!-- 接收文件的目標服務器可以支持寫入操作。 -->
        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

</web-app>

tomcat配置:
在這裏插入圖片描述

在這裏插入圖片描述

另外,文件服務器發佈到tomcat運行後,新建文件夾:

在這裏插入圖片描述

完善應用服務器代碼

maven中加入座標:

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>


<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>

<!--springmvc跨服務器文件上傳 -->

<!--    &lt;!&ndash; https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server &ndash;&gt;-->
<!--    <dependency>-->
<!--      <groupId>org.glassfish.jersey.core</groupId>-->
<!--      <artifactId>jersey-server</artifactId>-->
<!--      <version>2.30.1</version>-->
<!--    </dependency>-->

<!--    &lt;!&ndash; https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client &ndash;&gt;-->
<!--    <dependency>-->
<!--      <groupId>org.glassfish.jersey.core</groupId>-->
<!--      <artifactId>jersey-client</artifactId>-->
<!--      <version>2.30.1</version>-->
<!--    </dependency>-->

<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-core -->
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-core</artifactId>
  <version>1.19.4</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-client -->
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-client</artifactId>
  <version>1.19.4</version>
</dependency>

src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@Controller
@RequestMapping("/user")
public class UserController {

    // 跨服務器文件上傳
    @RequestMapping("/fileUpload3")
    public String fileUpload3(MultipartFile upload) throws IOException {
        // 注意:MultipartFile的名稱upload必須和前端 <input type="file" name="upload" /> 的name名稱一樣

        // 定義上傳服務器路徑
        String path = "http://127.0.0.1:9000/uploads/";  // 這裏根據實際情況,和文件服務器保持一致

        String filename = upload.getOriginalFilename();  // 獲取上傳文件名稱
        String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        filename = uuid + "_" + filename;
        // 完成文件上傳——跨服務器上傳

        // 創建客戶端對象
        Client client = Client.create();
        // 和圖片服務器進行連接
        WebResource webResource = client.resource(path + filename);
        // 上傳文件
        webResource.put(upload.getBytes());

        return "success";
    }
}

jsp頁面:

<h2>跨服務器文件上傳</h2>
<form action="user/fileUpload3" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="upload" /><br/>
    <input type="submit" value="上傳">
</form>

其他參考方式:

@Controller("fileUploadController2")
public class FileUploadController2 {
	public static final String FILESERVERURL = "http://localhost:9090/day06_spring_image/uploads/";
	/**
	* 文件上傳,保存文件到不同服務器
	*/
	@RequestMapping("/fileUpload2")
	public String testResponseJson(String picname,MultipartFile uploadFile) throws Exception{
		//定義文件名
		String fileName = "";
		//1.獲取原始文件名
		String uploadFileName = uploadFile.getOriginalFilename();
		//2.截取文件擴展名
		String extendName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1, uploadFileName.length());
		//3.把文件加上隨機數,防止文件重複
		String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
		//4.判斷是否輸入了文件名
		if(!StringUtils.isEmpty(picname)) {
			fileName = uuid+"_"+picname+"."+extendName;
		} else {
			fileName = uuid+"_"+uploadFileName;
		}
		System.out.println(fileName);
		//5.創建 sun 公司提供的 jersey 包中的 Client 對象
		Client client = Client.create();
		//6.指定上傳文件的地址,該地址是 web 路徑
		WebResource resource = client.resource(FILESERVERURL+fileName);
		//7.實現上傳
		String result = resource.put(String.class,uploadFile.getBytes());
		System.out.println(result);
		return "success";
	}
}

同樣,可以配置文件上傳解析器,同上。

第3章 SpringMVC 中的異常處理

3.1 異常處理的思路

系統中異常包括兩類:預期異常和運行時異常 RuntimeException,前者通過捕獲異常從而獲取異常信息,後者主要通過規範代碼開發、測試通過手段減少運行時異常的發生。

系統的 dao、 service、 controller 出現都通過 throws Exception 向上拋出,最後由 springmvc 前端控制器交由異常處理器進行異常處理。

在這裏插入圖片描述

前端控制器不作處理:

在這裏插入圖片描述
前端控制器做處理:

在這裏插入圖片描述

前端處理器不作處理:

在這裏插入圖片描述

3.2 異常處理步驟

src/main/java/top/onefine/exception/SysException.java:

package top.onefine.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

/**
 * 自定義異常類
 */
@Getter
@Setter
@AllArgsConstructor
public class SysException extends Exception {
    // 存儲提示信息
    private String message;
}

src/main/webapp/WEB-INF/pages/error.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>出錯頁面</title>
</head>
<body>
<h2>出錯啦!</h2> <hr/>
<p>${ errorMsg }</p>
</body>
</html>

自定義異常處理器——src/main/java/top/onefine/exception/SysExceptionResolver.java:

package top.onefine.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 異常處理器
 */
public class SysExceptionResolver implements HandlerExceptionResolver {
    // 處理異常業務邏輯
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        // 獲取到異常對象
        SysException ex = null;
        if (e instanceof SysException)
        	// 這裏如果拋出的是自定義異常則直接轉換
            ex = (SysException) e;
        else
            // ...處理其他類型的異常,這裏根據實際業務修改
            // 這裏如果拋出的不是自定義異常則重新構造一個自定義錯誤異常
            ex = new SysException("系統正在維護...");

        ModelAndView mv = new ModelAndView();
        mv.addObject("errorMsg", ex.getMessage());
        mv.setViewName("error");  // 跳轉到error.jsp頁面
        return mv;
    }
}

配置異常處理器——src/main/resources/springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描,配置spring創建容器時要掃描的包 -->
    <context:component-scan base-package="top.onefine" />

    <!-- 配置視圖解析器對象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />  <!-- prefix表示文件所在的位置 -->
        <property name="suffix" value=".jsp" />  <!-- suffix表示文件的後綴名 -->
    </bean>

    <!-- 配置前端控制器:靜態資源不攔截(過濾) -->
    <mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
    <mvc:resources mapping="/css/**" location="/WEB-INF/css/" />
    <mvc:resources mapping="/images/**" location="/WEB-INF/images/" />

    <!-- 配置異常處理器對象 -->
    <bean id="sysExceptionResolver" class="top.onefine.exception.SysExceptionResolver" />

    <!-- 配置Spring開啓註解MVC的支持 -->
    <mvc:annotation-driven />

</beans>

src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import top.onefine.exception.SysException;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/testException")
    public String testException() throws SysException {
        // 拋出異常,給前端控制器處理
        System.out.println("testException方法執行了...");

        try {
            // 模擬異常
            int a = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            // 拋出自定義異常信息
            throw new SysException("出錯啦!拋出異常...");
        }
        return "success";
    }
}

jsp頁面:

<a href="user/testException">異常處理</a>

在這裏插入圖片描述

第4章 SpringMVC 中的攔截器

4.1 攔截器的作用

Spring MVC 的處理器攔截器類似於 Servlet 開發中的過濾器 Filter,用於對處理器進行預處理和後處理。用戶可以自己定義一些攔截器來實現特定的功能。

談到攔截器,還要向大家提一個詞——攔截器鏈(Interceptor Chain)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。

說到這裏,可能大家腦海中有了一個疑問,這不是我們之前學的過濾器嗎?是的它和過濾器是有幾分相似,但是也有區別,接下來我們就來說說他們的區別:

  • 過濾器是 servlet 規範中的一部分, 任何 java web 工程都可以使用。
  • 攔截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
  • 過濾器在 url-pattern 中配置了/*之後,可以對所有要訪問的資源攔截。
  • 攔截器它是隻會攔截訪問的控制器方法,如果訪問的是 jsp,html,css,image 或者 js 是不會進行攔截的。

它也是 AOP 思想的具體應用。我們要想自定義攔截器, 要求必須實現: HandlerInterceptor 接口。
在這裏插入圖片描述

  1. SpringMVC框架中的攔截器用於對處理器進行預處理和後處理的技術。
  2. 可以定義攔截器鏈,連接器鏈就是將攔截器按着一定的順序結成一條鏈,在訪問被攔截的方法時,攔截器鏈中的攔截器會按着定義的順序執行。

4.2 自定義攔截器的步驟

src/main/webapp/index.jsp:

<a href="user/testInterceptor">攔截器</a>

src/main/webapp/WEB-INF/pages/success.jsp:

<% System.out.println("success.jsp執行了..."); %>

src/main/java/top/onefine/controller/UserController.java:

package top.onefine.controller;

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

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/testInterceptor")
    public String testInterceptor() {
        System.out.println("testInterceptor方法執行了...");
        return "success";
    }
}

src/main/java/top/onefine/interceptor/MyInterceptor1.java:

package top.onefine.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定義攔截器
 */
public class MyInterceptor1 implements HandlerInterceptor {

    /**
     * 預處理:controller方法執行前執行
     * @param request
     * @param response
     * @param handler
     * @return
     *          true放行,執行下一個攔截器;若沒有下一個攔截器了則執行controller中的方法
                false攔截,不放行,進行其他處理
     *
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle執行了...1");
        return true;

        // 進行攔截
//        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);  // 請求轉發
//        return false;
    }

    /**
     * 後處理:controller方法執行後執行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法執行了...1");
//        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);  // 請求轉發
    }

    /**
     * 頁面執行後,該方法執行
     * 可用於釋放資源
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法執行了...1");
        // 不能再請求轉發了
    }
}

放行的含義是指,如果有下一個攔截器就執行下一個,如果該攔截器處於攔截器鏈的最後一個,則執行控制器中的方法。

src/main/java/top/onefine/interceptor/MyInterceptor2.java:

package top.onefine.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor2 implements HandlerInterceptor {
	/**
		如何調用:
		* 按攔截器定義順序調用
		
		何時調用:
		* 只要配置了都會調用
		
		有什麼用:
		* 如果程序員決定該攔截器對請求進行攔截處理後還要調用其他的攔截器,或者是業務處理器去進行處理,則返回 true。
		* 如果程序員決定不需要再調用其他的組件去處理請求,則返回 false。
	*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle執行了...2");
        return true;
    }

	/**
		如何調用:
		* 按攔截器定義逆序調用

		何時調用:
		* 在攔截器鏈內所有攔截器返成功調用

		有什麼用:
		* 在業務處理器處理完請求後,但是 DispatcherServlet 向客戶端返回響應前被調用,在該方法中對用戶請求 request 進行處理。
	*/
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法執行了...2");
    }

	/**
		如何調用:
		* 按攔截器定義逆序調用
		
		何時調用:
		* 只有 preHandle 返回 true 才調用
	
		有什麼用:
		* 在 DispatcherServlet 完全處理完請求後被調用,
		* 可以在該方法中進行一些資源清理的操作。
	*/
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法執行了...2");
    }
}

src/main/resources/springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描,配置spring創建容器時要掃描的包 -->
    <context:component-scan base-package="top.onefine" />

    <!-- 配置視圖解析器對象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />  <!-- prefix表示文件所在的位置 -->
        <property name="suffix" value=".jsp" />  <!-- suffix表示文件的後綴名 -->
    </bean>

    <!-- 配置前端控制器:靜態資源不攔截(過濾) -->
    <mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
    <mvc:resources mapping="/css/**" location="/WEB-INF/css/" />
    <mvc:resources mapping="/images/**" location="/WEB-INF/images/" />

    <!-- 攔截器配置,配置攔截器的作用範圍 -->
    <mvc:interceptors>
        <!-- 配置第一個攔截器 -->
        <mvc:interceptor>
            <!-- 指定要攔截的url -->
            <mvc:mapping path="/user/**"/><!-- 攔截/user下面的所有url -->
            <!-- 指定不要攔截的url -->
<!--            <mvc:exclude-mapping path=""/>-->
            <!-- 配置攔截器對象 -->
            <bean class="top.onefine.interceptor.MyInterceptor1" />
        </mvc:interceptor>
        <!-- 配置第二個攔截器 -->
        <mvc:interceptor>
            <!-- 指定要攔截的具體方法 -->
            <mvc:mapping path="/**"/><!-- 攔截所有 -->
            <!-- 配置攔截器對象 -->
            <bean class="top.onefine.interceptor.MyInterceptor2" />
        </mvc:interceptor>
        <!--配置其他攔截器...-->
    </mvc:interceptors>
    
    <!-- 配置Spring開啓註解MVC的支持 -->
    <mvc:annotation-driven />

</beans>

注意:

  1. preHandle方法是controller方法執行前攔截的方法
    + 可以使用request或者response跳轉到指定的頁面
    + return true放行,執行下一個攔截器,如果沒有攔截器,執行controller中的方法。
    + return false不放行,不會執行controller中的方法。
  2. postHandle是controller方法執行後執行的方法,在JSP視圖執行前。
    + 可以使用request或者response跳轉到指定的頁面
    + 如果指定了跳轉的頁面,那麼controller方法跳轉的頁面將不會顯示。
  3. postHandle方法是在JSP執行後執行
    + request或者response不能再跳轉頁面了

多個攔截器的執行順序:多個攔截器是按照配置的順序決定的。

在這裏插入圖片描述

在這裏插入圖片描述

4.3 攔截器的簡單案例(驗證用戶是否登錄)

實現思路

  • 1、 有一個登錄頁面,需要寫一個 controller 訪問頁面

  • 2、 登錄頁面有一提交表單的動作。需要在 controller 中處理。

    • 2.1、 判斷用戶名密碼是否正確
    • 2.2、 如果正確 向 session 中寫入用戶信息
    • 2.3、 返回登錄成功。
  • 3、 攔截用戶請求,判斷用戶是否登錄

    • 3.1、 如果用戶已經登錄。放行
    • 3.2、 如果用戶未登錄,跳轉到登錄頁面

控制器代碼

//登陸頁面
@RequestMapping("/login")
public String login(Model model)throws Exception{
	return "login";
}

//登陸提交
//userid:用戶賬號, pwd:密碼
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
	//向 session 記錄用戶身份信息
	session.setAttribute("activeUser", userid);
	return "redirect:/main.jsp";
}

//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
	//session 過期
	session.invalidate();
	return "redirect:index.jsp";
}

攔截器代碼

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;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章