【萬字長文】Spring MVC 層層遞進輕鬆入門 !

SpringMVC 開篇絮叨

(一) 談一談 Web 項目開發

Html是“名詞”,CSS是“形容詞”,JavaScript是“動詞”,這三個兄弟湊在一起,就構成了 “靜態” 頁面,那麼如何讓他 “動態” 起來呢?這就需要後端相關技術的支持,這也是我們今天想要說的。

那麼又怎麼去理解 “靜態”“動態” 這兩個詞呢?

這兩個詞最大的不同就是在於其交互性,靜態頁面不是指頁面不能進行變化,而是指不能與後端進行交互,實現數據的傳輸與處理,也就是說,靜態頁面一旦做好後,基本就是這個樣子了,更像一個單純的展示,而動態頁面卻可以實現根據用戶的要求和選擇而動態的去改變和響應,瀏覽器客戶端,成爲了前後端動態交互的一個橋樑。

而隨着現在用戶需求的增加,以及數據量的增加,在Web開發中,能夠及時、正確地響應用戶的請求幾乎已經可以說是必須的了

  • ① 用戶在前端的頁面上,進行一個提交或者說點擊 URL,就會向後端服務器發送一個請求
  • ② 後端經過一系列處理後(例如,從數據庫中查到需要的數據)把數據響應給前端頁面
  • ③ 前端頁面獲取到響應內容後,對其進行解析以及進行一些處理(例如:回顯內容到頁面)

今天重點要學習的就是也就是——如何在獲取請求後對其解析,然後執行相關的邏輯處理,最終跳轉到頁面,將數據回饋

(二) 三層架構

上面我提到了,在前後端動態交互中,瀏覽器客戶端,成爲了前後端溝通的橋樑,這也就是常見的 B/S 架構方式,也就是 瀏覽器/服務器,在其中最爲常用的就是三層架構的開發模式

大家在 JavaWeb 的學習過程中,基本上已經在用三層架構的模式來進行編程,哪三層呢?

注:以JavaWeb中爲例

① 表現層(Web層)

  • 作用:接收客戶端請求(一般是HTTP請求),同時向其響應結果
  • 分類:表現層分爲,展示層和控制層,控制層 (Servlet) 負責接收請求,展示層 (HTML JSP) 負責結果的展示
  • 在表現層會依賴於業務層,進行業務處理,也就是好比在 Servlet 中調用某個Service
  • 一般使用 MVC 模型開發(僅限此層,詳情下面會說)

② 業務層(Service層)

  • 作用:根據項目需求,進行業務邏輯處理
  • 在業務層可能會依賴於持久層,也就是好比在 Service 中調用某個 Dao

③ 持久層 (Dao)

  • 作用:數據持久化
  • 說白了,就是實現和數據庫之間的交互,本質都是增刪改查,只不過不同的項目複雜程度會有所不同

有兩點需要強調一下:

什麼是,某某層依賴於某某層?

例如表現層依賴業務層,在 JavaWeb 階段實際上就是在 Servlet 中 new 了一個 Service ,當然,在Spring的 IOC 下我們只需要在控制層中添加Service的引用就可以了,並不需要再new了,耦合大大降低,我們上面說的依賴主要指兩個層之間存在一定的關係

什麼是業務邏輯?

針對,一些簡單的操作,例如單表數據的增刪,實際上幾乎沒有任何業務,最多例如參數不合法一類的,能加個返回的錯誤碼,但如果面對一些比較複雜的項目,就存在一些業務邏輯需要編寫

例如:查詢時需要的結果,並不是簡單的一張表中,而查詢條件也比較複雜,我們就可以通過對查詢條件進行拆分,再組合,就可以查詢到不同需求的數據。

再例如:以前文章中我常說的轉賬案例,爲了避免在轉賬的整個過程中發生異常,導致資金髮生問題,就需要保證事務的一致性,而這些事務我們就可以放在業務層來做,當然 Spring 的AOP 可以幫助我們更好的處理事務問題

(三) MVC 模型

MVC 也就是 model-view-controller,我們來看看它的每一部分

Model(模型)

  • Model 可以叫做數據模型層,也就是用來封裝數據的
  • 例如請求的過程中,用戶信息被封裝在 User 實體類中,這個實體類就屬於 Model 層中

View(視圖)

  • 視圖層中會選擇一個恰當的視圖來顯示最終的執行結果
  • 例如常見的 HTML JSP 就是用來展示數據的

Controller(控制)

  • 這就是比較直觀的用來處理交互的部分,接收用戶請求,然後執行業務等流程,以及一些數據的校驗,最終反饋結果

做了一張 MVC 模式下的工程結構圖,方便大家理解

初識 Spring MVC

實際上,如果是初次接觸 Spring MVC 實際上,看個基本概念也就行了,比如下面我提到的,Spring MVC 的優點,Spring MVC 與 Struts 的區別,如果在沒有進行過一些基本的使用,以及簡單一些流程的簡單分析,實際上沒啥卵用,這些東西簡單看看就行了,經過一定的學習以後,回過頭來再看,會有感覺的多

(一) Spring MVC 基礎

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.

—— Spring官網

Spring MVC屬於SpringFrameWork的後續產品,已經融合在Spring Web Flow裏面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用Spring進行WEB開發時,可以選擇使用Spring的Spring MVC框架或集成其他MVC開發框架,如Struts1(現在一般不用),Struts 2(一般老項目使用)等。

—— 百度百科

MVC 在上面我們已經進行了基本的介紹,而Spring MVC 就是一款基於 MVC架構模式的輕量級Web框架,我們所說的 Spring MVC 與 Spring Web MVC 是等價的,只不過人們更習慣前者的叫法,這一款框架,本質上也是基於 Servlet 的,如果你有 Servlet 以及 Spring 的基礎,簡單的上手這個Web框架是非常快的

(二) Spring MVC 的優點

  • ① Spring MVC 具有 Spring 的優點,例如依賴注入 (IOC) 和切面編程 (AOP)

  • ② 清晰的模塊化職能劃分,各模塊各司其職,清晰明瞭

    • 控制器 (controller)
    • 驗證器 (validator)
    • 命令對象 (command obect)
    • 表單對象 (form object)
    • 模型對象 (model object)
    • Servlet分發器 (DispatcherServlet)
    • 處理器映射 (handler mapping)
    • 試圖解析器 (view resoler)
  • ③ 可以非常方便的與其他視圖技術 (FreeMarker) 整合,由於Spring MVC 的模型數據往往放在 Map 數據結構中,因此可以很方便的被其他框架引用

  • ④ 可以靈活的實現綁定 (binding) 、驗證 (validation)

  • ⑤ 簡介的異常處理機制

  • ⑥ 比較強大的 JSP 標籤庫,簡化了JSP的開發

  • ⑦ 支持 RESTful 風格

  • ⑧ 提供了強大的約定大於配置的契約式編程支持,也就是提供一種軟件設計範式,減少軟件開發人員做決定的次數,開發人員僅需要規定應用中不符合約定的部分

(三) Spring MVC 與 Struts 的區別

Struts 也是一款基於 MVC 這種在開發模式的 JavaEE框架,近些年來,實際上開發者更多的選擇使用 SpringMVC 這個框架,那麼兩者的區別是什麼呢?Spring MVC 的過人之處又在哪裏呢?

① Spring MVC 基於方法開發,Struts 基於類開發

  • 使用 Spring MVC 開發的時候,會將 URL 請求的路徑與 Controller 的某個方法進行綁定,請求參數作爲該參數方法的形參
  • 使用 Struts 開始的時候,Action 類中所有方法使用的請求參數都是 Action 類中的成員變量,一旦方法變多,很容易混淆成員變量對應使用的方法

② Spring MVC 支持單例開發模式,而 Struts 不支持

③ Spring MVC 的速度比 Struts 的速度稍微快一些

  • 一是由於 Struts 每次都會創建一個動作類
  • 二是由於 Struts 的標籤設計問題

④ Spring MVC 使用更加簡潔,同時還支持 JSR303,能夠比較方便的處理 ajax

  • JSR 303 – Bean Validation (後臺通用參數校驗)

⑤ Struts2 的 OGNL 表達式使頁面的開發效率相比 Spring MVC 更高一點,但是執行效率對於 JSTL 也沒有很明顯的提升

淺嘗 Spring MVC

(一) 搭建開發環境

(1) 創建項目

① 創建Maven項目 --> ② 選擇JDK版本 --> ③ 勾選 create from archetype 即使用骨架創建項目 --> ④ 選擇 maven-archetype-webapp 創建出一個web項目

然後指定基本信息,點擊下一步

但是,由於創建 maven archetype 的原因,在創建時,會執行 mvn archetype:generate這個命令,這樣就需要指定一個 archetype-catalog.xml 文件,命令中參數 -DarchetypeCatalog 的值有三種

  • remote:從Maven遠程中央倉庫獲取 archetypeCatalog(默認的)
  • internal:從 maven-archetype-plugin 內置的 archetypeCatalog 文件獲取
  • local:本地的 archetypeCatalog 文件

我們需要做的就是添加這樣一組鍵值對,就可以加快創建項目的速度

  • DarchetypeCatalog
  • internal

這裏沒什麼好說的,基本不需要更改,繼續下一步

(2) 修改pom文件

將版本從1.7改爲1.8,接着又在 dependencies 中引入我們需要的一些 jar 包

定義 <spring.version>5.0.2.RELEASE</spring.version> 這樣一個標籤對,在下面就可以引用,這樣相比於直接將版本信息寫到每一個 dependencie 中,更利於後期的維護,方便更換版本,這種方式叫做鎖定版本

<?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>cn.ideal</groupId>
  <artifactId>spring_mvc_01_basic</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>spring_mvc_01_basic 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.version>5.0.2.RELEASE</spring.version>
  </properties>

  <dependencies>
    <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>

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

  </dependencies>


  <build>
    <finalName>spring_mvc_01_basic</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>

(3) 目錄結構

剛創建好的項目中,main文件夾下是空的,我們需要創建出 java 以及 resources 兩個文件夾,並且分別設置爲,源代碼根目錄 以及 資源根目錄,設置方式如下圖

(二) 編寫入門程序

(1) 配置核心控制器

在以前 JavaWeb 階段中,我們都很清楚,前端發出的請求,都會被映射到 Web.xml 中,然後匹配到對應的 Servlet 中,然後調用對應的 Servlet 類 來處理這個請求

由於現在我們使用了 Spring MVC,所以這些請求,我們就交給 Spring MVC 進行管理,所以需要在工程 webapp-WEB-INF 中找到 web.xml 進,在其中配置核心控制器,也就是 DispatcherServelt

<servlet ></servlet >標籤中指定了一個實現類爲 DispatcherServelt ,名稱爲 dispatcherServlet 的 servlet 配置

<servlet-mapping></servlet-mapping>標籤中則指定了 dispatcherServlet 攔截請求的範圍,使用 / 即代表所有請求都需要經過這裏

<init-param></init-param>標籤對中放置 DispatcherServelt 所需要的初始化參數,配置的是 contextConfigLocation 上下文參數變量,其加載的配置文件爲編譯目錄下的 springmvc.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>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>
    <!-- 配置servlet啓動時加載對象-->
    <load-on-startup>1</load-on-startup>

  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

(2) 創建核心配置文件

在這裏,一個是開啓掃描,以及開啓註解,還有就是配置視圖解析器,它的作用就是執行方法後,根據返回的信息,來加載相應的界面,並且綁定反饋數據

<?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="cn.ideal"></context:component-scan>

    <!-- 配置視圖解析器-->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

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

特別說明:一般開發我們都需要寫上這個標籤,即使或許現在還沒怎麼體現出來

(3) 編寫控制類

package cn.ideal.controller;

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

@Controller
public class ControllerDemo {
    @RequestMapping(path = "/test")
    public String methodTest(){
        System.out.println("這是Controller測試方法");
        return "testSuccess";
    }
}

(4) 編寫頁面

index.jsp

寫一個超鏈接,去請求test這個路徑,也就是指向到了 Controller 下的 methodTest() 方法

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>這是主頁面</h3>
    <a href="test">訪問test試試</a>
</body>
</html>

WEB-INF -> pages

testSuccess.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>跳轉成功哈</h3>
</body>
</html>

(5) 配置 Tomcat

我這裏,配置了本地的tomcat,以及項目名稱

(三) Spring MVC 請求流程

前端控制器(DispatcherServlet)

  • 接收用戶請求,以及做出響應
  • 它負責調用其他組件處理用戶的請求,控制整個流程的執行,想當於一箇中央處理器
  • 它降低了組件之間的耦合行,利於組件之間的擴展

處理器映射器(HandlerMapping)

  • 根據用戶請求的 URL 路徑,通過註解或者 XML 配置,尋找匹配的 Handler 即處理器

處理器適配器(HandlerAdapter)

  • 根據映射器找到的處理器(Handler)信息,按照特定規則執行相關的 Handler (常稱爲 Controller)

處理器(Hander)

  • 這就是開發中要編寫的具體業務邏輯控制器,執行相關的請求處理邏輯,並且返回相應的數據和視圖信息,然後封裝到 ModeAndView 對象中

視圖解析器(View resolver)

  • 通過ModelAndView 對象中的 View 信息將邏輯視圖名解析成物理視圖名,即具體的頁面地址,然後再生成 View 視圖對象,最後對 View 進行渲染處理結果通過頁面展示給用戶

視圖(View)

  • 本身是一個接口,實現類支持不同 View 類型 (JSP、FreeMarker、Excel 等)

注:我們開發人員真正需要進行開發的是處理器(Handler)和視圖(View)

也就是,處理用戶請求的具體邏輯代碼,以及展示給用戶的界面

(四) 請求映射與參數綁定

(1) RequestMapping

@RequestMaspping 註解是指定控制器可以處理哪些URL請求,這個註解可以放在類或者方法上。

  • 類上:一級訪問目錄
  • 方法上:二級訪問目錄
  • ${ pageContext.request.contextPath }可以省略不寫,但路徑上不能寫/

屬性:

  • path:指定請求路徑的url
  1. value:value屬性和path屬性是一樣的
  2. mthod:指定該方法的請求方式
  3. params:指定限制請求參數的條件
  4. headers:發送的請求中必須包含的請求頭

而一般不在 @RequestMaspping 中配置其他屬性的時候,可以省去 value 參數名,直接寫一個代表 URL 映射信息的字符串就可以了

例如:@RequestMaspping(/test)

(2) 請求參數的綁定

在用戶在頁面中出發請求的時候,提交表單的數據一般都是 key/value 格式的數據

在傳統JavaWeb 中我們所使用的一般是 request.getParameter() 等方法將請求參數獲取到

而Spring MVC中可以通過參數綁定,將客戶端請求的這個 key/value 格式的數據綁定到 Controller 處理器方法的形參上,支持的數據類型我們可以分爲三類

A:基本數據類型和字符串類型

index.jsp

注:只截取了部分

<h3>這是主頁面</h3>
<a href="user/testA?username=admin&password=admin888">測試一下</a>

pages --> testSuccess.jsp

<h3>跳轉成功哈</h3>

UserController

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

    @RequestMapping("/testA")
    public String testA(String username, String password) {
        System.out.println("獲取到的username: " + username);
        System.out.println("獲取到的password: " + password);
        return "testSuccess";
    }

}

通過構建一個超鏈接的方式傳遞參數,例如 ?username=admin 而在後端中如果方法形參與這個username是一致的,這個提交的數據就會被綁定到參數username中

B:JavaBean 實體類型

參數中使用 JavaBean 類型接收時,在提交表單的時候,就需要將其中的 name 屬性中的值與實體類中的成員變量的值是一樣的

如果一個JavaBean類中包含其他的引用類型,那麼表單的name屬性需要編寫成:對象.屬性例如:account.username

index.jsp

<form action="user/testB" method="post">
	暱稱: <input type="text" name="nickname"><br/>
	年齡: <input type="text" name="age"><br/>
    住址: <input type="text" name="address"><br/>
    用戶名: <input type="text" name="account.username"><br/>
    密碼: <input type="text" name="account.password"><br/>
    <input type="submit" value="提交">
</form>

UserController

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

    @RequestMapping("/testB")
    public String testB(User user) {
        System.out.println(user);
        return "testSuccess";
    }
    
}

實體類 User 和 Account

public class User implements Serializable {
    private String nickname;
    private int age;
    private String address;
    private Account account;
    ......省略 get set toString方法
}
public class Account implements Serializable {
    private String username;
    private String password;
    ......省略 get set toString方法
}

C:集合數據類型

對於集合類型,仍然使用一個表達式的寫法

index.jsp

<form action="user/testB" method="post">
    暱稱: <input type="text" name="nickname"><br/>
    年齡: <input type="text" name="age"><br/>
    住址: <input type="text" name="address"><br/>
    用戶名1: <input type="text" name="list[0].username"><br/>
    密碼1: <input type="text" name="list[0].password"><br/>
    用戶名2: <input type="text" name="map['First'].username"><br/>
    密碼2: <input type="text" name="map['First'].password"><br/>
    <input type="submit" value="提交">
</form>

UserController

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

    @RequestMapping("/testB")
    public String testB(User user) {
        System.out.println(user);
        return "testSuccess";
    }
    
}

實體類 User 和 Account

public class User implements Serializable {
    private String nickname;
    private int age;
    private String address;

    private List<Account> list;
    private Map<String, Account> map;
    ......省略 get set toString方法
}
public class Account implements Serializable {
    private String username;
    private String password;
    ......省略 get set toString方法
}

解決請求參數中文亂碼的問題

在 web.xml 中的 <web-app></web-app>標籤內配置過濾器類,達到解決請求參數中文亂碼的問題

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

(五) 常用註解

(1) RequestParam 註解

  • 作用:把請求中的指定名稱的參數傳遞給控制器中的形參

  • 屬性

    • value:請求參數中的名稱

    • name:name是value的別名,一個賬戶

    • required:是否必需,默認爲 true,即 請求中必須包含該參數,如果沒有包含,將會拋出異常(可選)

@RequestMapping(path="/hello")
public String sayHello(@RequestParam(value="nick",required=false)String nickname) {
	System.out.println(nickname);
	return "success";
}

(2) RequestBody 註解

  • 作用:用於獲取請求體的內容(注:get方法不可以)
@RequestMapping("/testC")
public String testC(@RequestBody String body) {
    System.out.println(body);
    return "testSuccess";
}

例如接收到這樣的語句

nickname=BWH_Steven&age=666&address=beijing

(3) PathVariable 註解

  • 作用:用於綁定url中的佔位符,例如:url中有/test/{id},{id}就是佔位符
  • 屬性:
    • value:用於指定url中佔位符名稱

UserController

@RequestMapping(path="/test/{uid}")
public String testD(@PathVariable(value="uid") String id) {
    System.out.println(id);
    return "testSuccess";
}

index.jsp

<a href="user/test/66">訪問test試試</a>

(4) RequestHeader 註解 (不常用)

  • 作用:獲取指定請求頭的值
  • 屬性:
    • value:請求頭的名稱

UserController

@RequestMapping("/testD")
public String testD(@RequestHeader(value="Accept") String header) {
    System.out.println(header);
    return "testSuccess";
}

index.jsp

<a href="user/testD">訪問test試試</a>

打印結果:

text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3

(5) CookieValue註解

  • 作用:用於獲取指定cookie的名稱的值

屬性:

  • value:cookie的名稱

UserController

@RequestMapping("/testF")
    public String testF(@CookieValue(value="JSESSIONID") String cookieValue) {
        System.out.println(cookieValue);
        return "testSuccess";
    }

index.jsp

<a href="user/testF">訪問test試試</a>

打印結果:

FCFDD389AC473F837266FC890E9E6F36

(6) ModelAttribute 註解

作用:

  • 在方法上:表示當前方法會在控制器方法執行前執行

  • 在參數上:獲取指定的數據給參數賦值

應用場景:

  • 提交表單的數據不是完整的數據,而沒提交的字段,就是用數據庫中原來的
  • 例如:用戶修改個人信息,但是暱稱則不允許修改,只提供修改例如年齡、地址等的表單,如果不進行任何的處理,就會導致,接收到的數據中 nickname 這個值是 null,再存到數據庫就會對原來的數據造成損失影響
  • 還有一些情況就例如:賬號註冊日期這種信息當然也是不能被修改的

index.jsp

只提供修改年齡和地址的表單,同時傳一個隱藏域中的id,方便去數據庫查詢(當然我們這裏是模擬的)

<form action="user/testG" method="post">
    <input type="hidden" name="uid" value="1">
    年齡: <input type="text" name="age"><br/>
    住址: <input type="text" name="address"><br/>
    <input type="submit" value="提交">
</form>

UserController

實體類就不給出了,就是三個成員,nickname age address

如果沒有下面這個增加了 @ModelAttribute 註解的 findUserByUid方法,當執行 testG 方法後,會獲取到一個 nickname = null 的值

而我們下面的做法,在執行 testG 之前會先執行 findUserByUid,然後可以去數據庫中根據uid查詢,當然我們這裏是模擬的,然後將這個user返回

接着執行 testG 方法的時候,就能將用戶提交的 age 和 address 獲取到,同時將用戶沒有提交的 nickname 使用數據庫中的值

@RequestMapping("/testG")
public String testG(User user) {
    System.out.println("這是testG方法");
    System.out.println("用戶修改後的: " + user);
    return "testSuccess";
}

@ModelAttribute
public User findUserByUid(Integer uid) {
    System.out.println("這是findUserByUid方法");
    //模擬查詢數據庫
    User user = new User();
    user.setNickname("BWH_Steven");
    user.setAge(66);
    user.setAddress("北京");

    System.out.println("數據庫中查詢到: " + user);

    return user;
}

另一種方式

如果沒有返回值的方式就可以這樣改寫,增加一個 map ,然後存進去

@RequestMapping("/testG")
public String testG(@ModelAttribute("first") User user) {
    System.out.println("這是testG方法");
    System.out.println("用戶修改後的: " + user);
    return "testSuccess";
}

@ModelAttribute
public void findUserByUid(Integer uid, Map<String,User> map) {
    System.out.println("這是findUserByUid方法");
    //模擬查詢數據庫
    User user = new User();
    user.setNickname("BWH_Steven");
    user.setAge(66);
    user.setAddress("北京");

    System.out.println("數據庫中查詢到: " + user);

    map.put("first",user);

}

(7) SessionAttributes 註解

  • 作用:用於多次執行控制器方法間的參數共享

屬性

  • value:指定存入屬性的名稱

UserController

在存入方法跳轉之前,會將數據保存到 nickname age address 中,因爲註解@SessionAttribute中有這幾個參數

@Controller
@RequestMapping("/user")
@SessionAttributes(value = {"nickname","age","address"})
public class UserController {
	/**
     * 向session中存入值
     * @param model
     * @return
     */
    @RequestMapping("/addUser")
    public String addUser(Model model) {
        model.addAttribute("nickname", "BWH_Steven");
        model.addAttribute("age", 20);
        model.addAttribute("address", "北京");
        return "testSuccess";
    }

    /**
     * 從session中獲取值
     * @param modelMap
     * @return
     */
    @RequestMapping("/findUser")
    public String findUser(ModelMap modelMap) {
        String nickname = (String) modelMap.get("nickname");
        Integer age = (Integer)modelMap.get("age");
        String address= (String) modelMap.get("address");
        System.out.println(nickname + " " + age + " " + address);
        return "testSuccess";
    }

    /**
     * 清除值
     * @return
     */
    @RequestMapping("/delete")
    public String delete(SessionStatus status) {
        status.setComplete();
        return "testSuccess";
    }
}

index.jsp

<a href="user/addUser">訪問addUser試試</a>
<a href="user/findUser">訪問findUser試試</a>
<a href="user/delete">訪問delete試試</a>

testSuccess.jsp

注意設置 isELIgnored=“false”

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>跳轉成功哈</h3>
    ${nickname}
    ${age}
    ${address}
    ${sessionScope}
</body>
</html>

(六) 響應數據以及結果視圖

講完了請求與參數綁定,以及一些常用的註解,接着就可以說一下響應的一些知識,也就是我們接受到用戶的請求,並且進行一定的處理以後,如何進行正確的響應

(1) 返回字符串

其實在前面的講解中,我們一直用的就是返回字符串的形式,而結果也是很直觀的,也就是,進行了同名頁面的跳轉,例如返回 success 則跳轉到 success.jsp 的頁面中

這也就是說,Controller 方法返回字符串可以指定邏輯視圖的名稱,視圖解析器會將其解析成物理視圖的地址

演示一種常見的使用場景

index.jsp

<a href="user/testString">修改用戶信息頁面</a>

UserController

注:實體類就不談了,只有 username 和 password 兩個成員

模擬一個數據庫查詢到數據信息,然後將數據存到 request 域中

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/testString")
    public String testString(Model model) {
        //模擬從數據庫中查詢
        User user = new User();
        user.setUsername("張三");
        user.setPassword("888666");
        model.addAttribute("user", user);
        return "success";
    }
}

success.jsp

注意配置:isELIgnored=“false”

當用戶點擊主頁面中進行頁面修改,就會跳轉到這個用戶名以及密碼的修改界面,同時將數據進行回顯,優化體驗

<%@ page contentType="text/html;charset=UTF-8" language="java"  isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>修改</h3>
    <form action="" method="post">
        用戶名:<input type="text" name="username" value="${ user.username }"><br>
        密碼:<input type="text" name="password" value="${ user.password }"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

A:響應轉發或者重定向

<a href="user/testForward">測試一下</a>
@RequestMapping("/testForward")
    public String testForward() throws Exception{
        System.out.println("testForward 被執行了");
          //轉發
//        return "forward:/WEB-INF/pages/success.jsp";
        //重定向
        return "redirect:/index.jsp";
    }

(2) 返回 void

如果說直接去掉返回值,以及修改返回類型爲void,會報出一個404異常,可以看到地址欄中,去指向了一個 http://localhost:8080/springmvc-response/user/testVoid.jsp 的地址,也就是說它默認去查找了一個jsp頁面(也就是 @RequestMapping("/testVoid") 值同名的 jsp),不過沒有找到

如果想要在這種情況下,跳轉頁面可以使用請求轉發,或者重定向跳轉

@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request,HttpServletResponse response) throws
Exception {
System.out.println("請求轉發或者重定向被執行了");
// 1. 請求轉發
// request.getRequestDispatcher("/WEB-INF/pages/test1.jsp").forward(request,
response);
    
// 2. 重定向
// response.sendRedirect(request.getContextPath()+"/test2.jsp");
    
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 3. 直接響應數據
response.getWriter().print("測試被執行了哈");
return;
}

(3) 返回值是ModelAndView對象

這種方式其實和String達到的效果基本是一致的

index.jsp

<a href="user/findAll">測試一下</a>

UserController

@RequestMapping("/findUser")
public ModelAndView findUser() throws Exception{
    ModelAndView modelAndView = new ModelAndView();
    //跳轉到jsp
    modelAndView.setViewName("success");

    //模擬從數據庫中查詢用戶信息
    User user = new User();
    user.setUsername("李四");
    user.setPassword("888888");
    
    modelAndView.addObject("user",user);
    return modelAndView;
}

success.jsp

${user.username}
${user.password}

(4) 過濾靜態資源(必備)

在 web.xml 中配置的 DispatcherServle(前端控制器),會攔截到所有的資源,在以後的開發中,一個特別顯著的問題就是,靜態資源 (img、css、js)這樣的文件也被攔截了,也就無法使用,我們首先需要了解的就是如何不對靜態資源進行攔截

非常簡單,在springmvc.xml中配置就可以了

mvc:resources 標籤就可以配置不過濾

  • location 表示webapp目錄下的包下的所有文件
  • mapping 表示以/xxx開頭的所有請求路徑,如/xxx/a 或者/xxx/a/b
<!--前端控制器-->
<mvc:resources mapping="/css/**/" location="/css/"/>
<mvc:resources mapping="/images/**/" location="/images/"/>
<mvc:resources mapping="/js/**/" location="/js/"/>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>

    <%--引入jquery--%>
    <script src="js/jquery-2.1.0.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                alert("Just for test");
            });
        });
    </script>
</head>
<body>
    <%--<a href="user/testString">修改用戶信息頁面</a>--%>
    <%--<a href="user/testForward">測試一下</a>--%>
    <button id="btn">發送ajax請求</button>
</body>
</html>

(5) 發送ajax請求-後臺獲取請求體

index.jsp

在 Javaweb 階段,大家基本都是有了解過 ajax 的,所以我就直接用了,如果有不熟悉的,可以去查一下api或者找一下教程,格式還是非常好理解的

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>

    <%--引入jquery--%>
    <script src="js/jquery-2.1.0.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                //發送ajax請求
                $.ajax({
                    url:"user/testAjax",
                    contentType:"application/json;charset=UTF-8",
                    data:'{"username":"zhangsan","password":"888888"}',
                    dataType:"json",
                    type:"post",
                    success:function (data) {
                        //解析響應數據
                    }
                })
            });
        });
    </script>
</head>
<body>
    <button id="btn">發送ajax請求</button>
</body>
</html>

參數中使用 @RequestBody 這個註解,就可以接收到請求體,然後變成這樣一個串的形式

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

    @RequestMapping("/testAjax")
    public void testAjax(@RequestBody String body){
        System.out.println("testAjax 被執行了");
        System.out.println(body);
    }
}

打印結果就是這樣的

testAjax 被執行了
{"username":"zhangsan","password":"888888"}

(6) 響應json格式數據

UserControllr

@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
    System.out.println("testAjax 被執行了");
    // 模擬數據庫查詢
    System.out.println(user);
    user.setUsername("admin");
    user.setPassword("admin888");
    return user;
}

使用 @RequestBody String body 接收到的是一個串,而想要直接將 Json 字符串和 JavaBean 對象相互轉換,需要 jackson 的jar包,我們可以增加這樣的依賴

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.0</version>
</dependency>

index.jsp

<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
    $(function () {
        $("#btn").click(function () {
            //發送ajax請求
            $.ajax({
                url:"user/testAjax",
                contentType:"application/json;charset=UTF-8",
                data:'{"username":"zhangsan","password":"888888"}',
                dataType:"json",
                type:"post",
                success:function (data) {
                    //解析響應數據
                    alert(data);
                    alert(data.username);
                    alert(data.password);
                }
            })
        });
     });
</script>

(七) 文件上傳

(1) 普通文件上傳方式

index.jsp

<h3>文件上傳</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
    選擇文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上傳文件"/>
</form>
  • form表單的enctype的默認值是:application/x-www-form-urlencoded

    • 表單正文內容一般是:key=value&key=value&key=value
  • 如果想要進行文件上傳,就必須要改爲 multipart/form-data(),同時method屬性取值必須是Post

    • 每一部分都變成MIME類型描述的正文

注意:當form表單的enctype取值不是application/x-www-form-urlencoded後,request.getParameter()方法就不能再使用

注意:想要實現文件上傳,可以藉助一些組件,需要導入該組件相應的支撐jar 包:Commons-fileupload 和commons-io

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

UserController

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

    @RequestMapping("/fileupload")
    public String fileupload(HttpServletRequest request) throws Exception {
        System.out.println("文件上傳...");

        // 使用fileupload組件完成文件上傳
        // 上傳位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判斷,該路徑是否存在
        File file = new File(path);
        if (!file.exists()) {
            // 不存在則創建文件夾
            file.mkdirs();
        }

        // 解析request對象,獲取上傳文件項
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 解析request
        List<FileItem> items = upload.parseRequest(request);
        // 遍歷
        for (FileItem item : items) {
            // 進行判斷,當前item對象是否是上傳文件項
            if (item.isFormField()) {
                // 普通表單項
            } else {
                // 上傳文件項
                // 上傳文件的名稱
                String filename = item.getName();
                // 把文件的名稱設置唯一值,UUID 防止重複覆蓋問題
                String uuid = UUID.randomUUID().toString().replace("-", "");
                filename = uuid + "_" + filename;
                // 完成文件上傳
                item.write(new File(path, filename));
                // 刪除臨時文件
                item.delete();
            }
        }

        return "success";
    }
}

說明

request.getSession().getServletContext() 獲取的是Servlet容器對象,就好比tomcat容器

getRealPath("/") 代表獲取實際路徑,“/”指代項目根目錄

所以代碼返回的是項目在容器中的實際發佈運行的根路徑

(2) Spring MVC 上傳方式(同服務器)

index.jsp

<h3>文件上傳</h3>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
    選擇文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上傳文件"/>
</form>

springmvc.xml

<!--配置文件解析器對象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="10485760" />
</bean>

注意:10485760 = 10x1024x1024

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

    @RequestMapping("/fileupload2")
    public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("文件上傳...");

        // 使用fileupload組件完成文件上傳
        // 上傳位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判斷,該路徑是否存在
        File file = new File(path);
        if (!file.exists()) {
            // 不存在則創建文件夾
            file.mkdirs();
        }

        // 獲取上傳文件的名稱
        String filename = upload.getOriginalFilename();
        // 把文件的名稱設置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid+"_"+filename;
        // 完成文件上傳
        upload.transferTo(new File(path,filename));

        return "success";
    }
}

(3) Spring MVC 上傳方式(跨服務器)

很多時候會將整個工程部署到不同的服務器,例如:

應用服務器,數據庫服務器,緩存和消息服務器,文件服務器等等,不過入門來說了解一下就可以了

想要測試下面的代碼,可以配置兩個 Tomcat 給不同端口等配置,模擬一下

index.jsp

<h3>文件上傳</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
    選擇文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上傳文件"/>
</form>

增加依賴

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-core</artifactId>
  <version>1.18.1</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-client</artifactId>
  <version>1.18.1</version>
</dependency>

UserController

@Controller
@RequestMapping("/user")
public class UserController {
    
   @RequestMapping("/fileupload3")
    public String fileupload3(MultipartFile upload) throws Exception {
        System.out.println("SpringMVC跨服務器方式的文件上傳...");
        // 定義圖片服務器的請求路徑
        String path = "http://localhost:9090//springmvc-fileupload/uploads/";
        // 獲取到上傳文件的名稱
        String filename = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        // 把文件的名稱唯一化
        filename = uuid + "_" + filename;
        // 向圖片服務器上傳文件
        // 創建客戶端對象

        Client client = Client.create();
        // 連接圖片服務器
        WebResource webResource = client.resource(path + filename);
        // 上傳文件
        webResource.put(upload.getBytes());
        return "success";
    }
}

(八) 異常處理

異常處理也算一個老生常談的問題,在上線項目或者運行項目的時候,總可能會出現一些無法預料的異常信息,對於開發者而言,自然需要看到具體的異常信息,然後進行排除,而對於用戶,自然儘可能的出現一些簡單,易於理解的語言或者提示

在 Spring MVC 中,提供了一個全局異常處理器,可以對異常進行統一處理

Dao、Service、Controller出現都通過 throws Exception 向上拋出,最後由Spring MVC前端
控制器交由全局異常處理器進行異常處理

(1) 自定義異常類

對於預期的異常,通常定義一個自定義異常類,用來存儲異常的信息

首先這個類繼承了 Exception 類,用來描述程序能獲取的異常,設置了一個成員message,就是用來存放異常信息的

package cn.ideal.exception;

public class SysException extends Exception {
    private String message;

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public SysException(String message) {
        this.message = message;
    }
}

(2) 全局異常處理器

全局異常處理器實現的是 Spring MVC 的 HandlerExceptionResolver 接口

這是接口的源碼

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
  • Exception ex:就是 Controller 或下層拋出的異常
  • Object handler:處理器適配器要執行的 Handler 對象
  • 返回值類型:ModelAndView 這也就是說,可以通過這個返回值設置異常時顯示的頁面
public class SysExceptionResolver implements HandlerExceptionResolver {
    /**
     * 跳轉到具體的錯誤頁面的方法
     */
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse
            response, Object handler,Exception ex) {
        ex.printStackTrace();
        //解析出異常類型
        SysException sysException = null;
        // 獲取到異常對象
        if (ex instanceof SysException) {
            //如果異常類型是系統自定義異常,直接取出異常信息,在錯誤頁面展示
            sysException = (SysException) ex;
        } else {
            //如果異常類型不是系統自定義異常,則構造一個自定義異常類型
            sysException = new SysException("未知錯誤");
        }
        ModelAndView mv = new ModelAndView();
        // 存入錯誤的提示信息
        mv.addObject("errorMsg", sysException.getMessage());
        // 跳轉的Jsp頁面
        mv.setViewName("error");
        return mv;
    }
}

(3) 配置異常處理器

springmvc.xml 中配置

<bean id="sysExceptionResolver" class="cn.ideal.exception.SysExceptionResolver"/>

(4) 測試代碼

UserController

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("testException")
    public String testException() throws SysException {
        System.out.println("testException被執行了");
        try {
            int a = 100 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new SysException("測試這個方法出錯了");
        }
        return "success";
    }
}

index.jsp

<h3>主頁面</h3>
<a href="user/testException">測試一下</a>

error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${errorMsg}
</body>
</html>

(九) 攔截器

攔截器,用來幹嘛呢,就比如你需要檢測用戶的權限,或者把請求信息記錄到日誌等等,也就是說需要在用戶請求前後執行的一些行爲

首先在 Spring MVC 中是有兩種機制,第一種就是實現 HandlerInterceptor接口,還有一種就是實現 Spring 的 WebRequestInterceptor 接口,不過我們就簡單說一下第一種

自定義一個類簡單看一下,實現三個方法

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法執行了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法執行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法執行了");
    }
}

① preHandle方法:controller 方法執行前攔截的方法

  • 可以使用 request 或者 response 跳轉到指定的頁面

  • return true放行,執行下一個攔截器,如果沒有攔截器,執行 controller 中的方法

  • return false不放行,不會執行 controller 中的方法

② postHandle:controller 方法執行後執行的方法,在 JSP 視圖執行前

  • 可以使用 request 或者 response 跳轉到指定的頁面

  • 如果指定了跳轉的頁面,那麼 controller 方法跳轉的頁面將不會顯示。

③ postHandle方法:在JSP執行後執行

  • request 或者 response 不能再跳轉頁面了

配置攔截器

注:不要攔截用這個標籤<mvc:exclude-mapping path=""/>

注:/user/* 代表所有訪問帶有 /user/的路徑都會被攔截,例如 /user/test

<!--配置攔截器-->
<mvc:interceptors>
    <!--配置攔截器-->
    <mvc:interceptor>
        <!--要攔截的具體的方法-->
        <mvc:mapping path="/user/*"/>
        <!--配置攔截器對象-->
        <bean class="cn.ideal.interceptor.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

隨便寫一個方法測試一下

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

執行結果

preHandle方法執行了
testInterceptor被執行了
postHandle方法執行了
afterCompletion方法執行了

總結

寫着寫着又是1w字了,之前就想着寫這篇文章,也一直沒什麼空,對於這一篇文章,我認爲對於入門來說還是比較有好的,前面給了幾個大點的基本知識講解,然後從開發環境以及一個入門程序開始,再到請求以及如何響應,以及一些常用的註解,再到其他的,文件上傳,異常處理,攔截器等知識,基本來說,達到了一個 工具書 + 入門講解的效果,不過要說的點太多了,即使1w字的文章,實際上也只夠簡單提及,再加個小案例,就例如攔截器,或者文件上傳的講解,只能說講了最基本的,對於已經有一定基礎的朋友,自然沒什麼進階的幫助,不過我的初心,也是想鞏固一下自己的知識,然後能將文章帶給剛接觸 Spring MVC 的朋友,我也不是什麼大牛,不過希望能給大家一點幫助,我們可以一起交流,一起進步哈!

感謝大家的支持!!!

結尾

如果文章中有什麼不足,歡迎大家留言交流,感謝朋友們的支持!

如果能幫到你的話,那就來關注我吧!如果您更喜歡微信文章的閱讀方式,可以關注我的公衆號

在這裏的我們素不相識,卻都在爲了自己的夢而努力 ❤

一個堅持推送原創開發技術文章的公衆號:理想二旬不止

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章