SpringMVC總結

SpringMVC

SpringMVC是隸屬于Spring框架的一部分,主要是用來進行Web開發,是對Servlet進行了封裝。

對于SpringMVC我們主要學習如下內容:

  • SpringMVC簡介

  • 請求與響應

  • REST風格

  • SSM整合(注解版)

  • 攔截器

SpringMVC是處理Web層/表現層的框架,所以其主要的作用就是用來接收前端發過來的請求和數據然后經過處理并將處理的結果響應給前端,所以如何處理請求和響應是SpringMVC中非常重要的一塊內容。

REST是一種軟件架構風格,可以降低開發的復雜性,提高系統的可伸縮性,后期的應用也是非常廣泛。

SSM整合是把咱們所學習的SpringMVC+Spring+Mybatis整合在一起來完成業務開發,是對我們所學習這三個框架的一個綜合應用。

對于SpringMVC的學習,最終要達成的目標:

  1. 掌握基于SpringMVC獲取請求參數和響應json數據操作
  2. 熟練應用基于REST風格的請求路徑設置與參數傳遞
  3. 能夠根據實際業務建立前后端開發通信協議并進行實現
  4. 基于SSM整合技術開發任意業務模塊功能

1. SpringMVC概述

學習SpringMVC我們先來回顧下現在web程序是如何做的,咱們現在web程序大都基于三層架構來實現。

三層架構:

img

  • 瀏覽器發送一個請求給后端服務器,后端服務器現在是使用Servlet來接收請求和數據

  • 如果所有的處理都交給Servlet來處理的話,所有的東西都耦合在一起,對后期的維護和擴展極為不利

  • 將后端服務器Servlet拆分成三層,分別是webservicedao

    • web層主要由servlet來處理,負責頁面請求和數據的收集以及響應結果給前端
    • service層主要負責業務邏輯的處理
    • dao層主要負責數據的增刪改查操作
  • servlet處理請求和數據的時候,存在的問題是一個servlet只能處理一個請求

  • 針對web層進行了優化,采用了MVC設計模式,將其設計為controllerviewModel

    • controller負責請求和數據的接收,接收后將其轉發給service進行業務處理
    • service根據需要會調用dao對數據進行增刪改查
    • dao把數據處理完后將結果交給service,service再交給controller
    • controller根據需求組裝成Model和View,Model和View組合起來生成頁面轉發給前端瀏覽器
    • 這樣做的好處就是controller可以處理多個請求,并對請求進行分發,執行不同的業務操作。

隨著互聯網的發展,上面的模式因為是同步調用,性能慢慢的跟不是需求,所以異步調用慢慢的走到了前臺,是現在比較流行的一種處理方式。

img

  • 因為是異步調用,所以后端不需要返回view視圖,將其去除

  • 前端如果通過異步調用的方式進行交互,后臺就需要將返回的數據轉換成json格式進行返回

  • SpringMVC主要負責的就是

    • controller如何接收請求和數據
    • 如何將請求和數據轉發給業務層
    • 如何將響應數據轉換成json發回到前端

介紹了這么多,對SpringMVC進行一個定義

  • SpringMVC是一種基于Java實現MVC模型的輕量級Web框架

  • 優點

    • 使用簡單、開發便捷(相比于Servlet)
    • 靈活性強

這里所說的優點,就需要我們在使用的過程中慢慢體會。

2.SpringMVC入門案例

2.1 SpringMVC實現流程:

SpringMVC的具體實現流程:

1.創建web工程(Maven結構)

2.設置tomcat服務器,加載web工程(tomcat插件)

3.導入坐標(SpringMVC+Servlet)

4.定義處理請求的功能類(UserController)

@Controller
public class UserController {@RequestMapping("/save")@ResponseBodypublic void save(){System.out.println("user save ...");}
}

5.設置SpringMVC的配置類

@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}

6.將SpringMVC設定加載到Tomcat容器中,設置請求映射(配置映射關系)

public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {//加載springmvc配置類protected WebApplicationContext createServletApplicationContext() {//初始化WebApplicationContext對象AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();//加載指定配置類ctx.register(SpringMvcConfig.class);return ctx;}//設置由springmvc控制器處理的請求映射路徑protected String[] getServletMappings() {return new String[]{"/"};}//加載spring配置類protected WebApplicationContext createRootApplicationContext() {return null;}
}

對于上述的配置方式,Spring還提供了一種更簡單的配置方式,可以不用再去創建AnnotationConfigWebApplicationContext對象,不用手動register對應的配置類,如何實現?

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {protected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}protected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}protected String[] getServletMappings() {return new String[]{"/"};}
}
知識點1:@Controller
名稱@Controller
類型類注解
位置SpringMVC控制器類定義上方
作用設定SpringMVC的核心控制器bean
知識點2:@RequestMapping
名稱@RequestMapping
類型類注解或方法注解
位置SpringMVC控制器類或方法定義上方
作用設置當前控制器方法請求訪問路徑
相關屬性value(默認),請求訪問路徑
知識點3:@ResponseBody
名稱@ResponseBody
類型類注解或方法注解
位置SpringMVC控制器類或方法定義上方
作用設置當前控制器方法響應內容為當前返回值,無需解析

2.2 入門案例總結

  • 一次性工作

    • 創建工程,設置服務器,加載工程
    • 導入坐標
    • 創建web容器啟動類,加載SpringMVC配置,并設置SpringMVC請求攔截路徑
    • SpringMVC核心配置類(設置配置類,掃描controller包,加載Controller控制器bean)
  • 多次工作

    • 定義處理請求的控制器類
    • 定義處理請求的控制器方法,并配置映射路徑(@RequestMapping)與返回json數據(@ResponseBody)

2.3 工作流程解析

為了更好的使用SpringMVC,我們將SpringMVC的使用過程總共分兩個階段來分析,分別是啟動服務器初始化過程單次請求過程

img

2.3.1 啟動服務器初始化過程
  1. 服務器啟動,執行ServletContainersInitConfig類,初始化web容器
    • 功能類似于以前的web.xml
  1. 執行createServletApplicationContext方法,創建了WebApplicationContext對象
    • 該方法加載SpringMVC的配置類SpringMvcConfig來初始化SpringMVC的容器
  1. 加載SpringMvcConfig配置類
    img

  2. 執行@ComponentScan加載對應的bean

    • 掃描指定包及其子包下所有類上的注解,如Controller類上的@Controller注解
  1. 加載UserController,每個@RequestMapping的名稱對應一個具體的方法
    img
    • 此時就建立了 /save 和 save方法的對應關系
  1. 執行getServletMappings方法,設定SpringMVC攔截請求的路徑規則
    img
    • /代表所攔截請求的路徑規則,只有被攔截后才能交給SpringMVC來處理請求
2.3.2 單次請求過程
  1. 發送請求http://localhost/save

  2. web容器發現該請求滿足SpringMVC攔截規則,將請求交給SpringMVC處理

  3. 解析請求路徑/save

  4. 由/save匹配執行對應的方法save()

    • 上面的第五步已經將請求路徑和方法建立了對應關系,通過/save就能找到對應的save方法
  1. 執行save()

  2. 檢測到有@ResponseBody直接將save()方法的返回值作為響應體返回給請求方

3. 請求與響應

SpringMVC是web層的框架,主要的作用是接收請求、接收數據、響應結果,所以這一章節是學習SpringMVC的重點內容,我們主要會講解四部分內容:

  • 請求映射路徑

  • 請求參數

  • 日期類型參數傳遞

  • 響應json數據

3.1 設置請求映射路徑

@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/save")@ResponseBodypublic String save(){System.out.println("user save ...");return "{'module':'user save'}";}@RequestMapping("/delete")@ResponseBodypublic String save(){System.out.println("user delete ...");return "{'module':'user delete'}";}
}@Controller
@RequestMapping("/book")
public class BookController {@RequestMapping("/save")@ResponseBodypublic String save(){System.out.println("book save ...");return "{'module':'book save'}";}
}

3.2 請求參數

GET發送參數及中文亂碼

發送請求與參數:

img

接收參數:

@Controller
public class UserController {@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name,int age){System.out.println("普通參數傳遞 name ==> "+name);System.out.println("普通參數傳遞 age ==> "+age);return "{'module':'commonParam'}";}
}

中文亂碼解決方案:

修改pom.xml

<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><!--tomcat端口號--><path>/</path> <!--虛擬目錄--><uriEncoding>UTF-8</uriEncoding><!--訪問路徑編解碼字符集--></configuration></plugin></plugins></build>
POST發送參數及中文亂碼

發送請求與參數:

img接收參數:

和GET一致,不用做任何修改

@Controller
public class UserController {@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name,int age){System.out.println("普通參數傳遞 name ==> "+name);System.out.println("普通參數傳遞 age ==> "+age);return "{'module':'commonParam'}";}
}

中文亂碼解決方案:

配置過濾器(在web容器配置類中)

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {protected Class<?>[] getRootConfigClasses() {return new Class[0];}protected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}protected String[] getServletMappings() {return new String[]{"/"};}//亂碼處理@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};}
}

3.3 五種類型參數傳遞

常見的參數種類有:

  • 普通參數

  • POJO類型參數

  • 嵌套POJO類型參數

  • 數組類型參數

  • 集合類型參數

3.3.1 普通參數

img

如果形參與地址參數名不一致該如何解決?

解決方案:使用@RequestParam注解

@RequestMapping("/commonParamDifferentName")@ResponseBodypublic String commonParamDifferentName(@RequestParam("name") String userName , int age){System.out.println("普通參數傳遞 userName ==> "+userName);System.out.println("普通參數傳遞 age ==> "+age);return "{'module':'common param different name'}";}

注意:寫上@RequestParam注解框架就不需要自己去解析注入,能提升框架處理性能

3.3.2 POJO數據類型

簡單數據類型一般處理的是參數個數比較少的請求,如果參數比較多,那么后臺接收參數的時候就比較復雜,這個時候我們可以考慮使用POJO數據類型。

  • POJO參數:請求參數名與形參對象屬性名相同,定義POJO類型形參即可接收參數

此時需要使用前面準備好的POJO類,先來看下User

public class User {private String name;private int age;//setter...getter...略
}

發送請求和參數:

img

后臺接收參數:

//POJO參數:請求參數與形參對象中的屬性對應即可完成參數傳遞
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){System.out.println("pojo參數傳遞 user ==> "+user);return "{'module':'pojo param'}";
}

注意:

  • POJO參數接收,前端GET和POST發送請求數據的方式不變。
  • 請求參數key的名稱要和POJO中屬性的名稱一致,否則無法封裝。
3.3.3 嵌套POJO類型參數

如果POJO對象中嵌套了其他的POJO類,如

public class Address {private String province;private String city;//setter...getter...略
}
public class User {private String name;private int age;private Address address;//setter...getter...略
}
  • 嵌套POJO參數:請求參數名與形參對象屬性名相同,按照對象層次結構關系即可接收嵌套POJO屬性參數

發送請求和參數:

img

后臺接收參數:

//POJO參數:請求參數與形參對象中的屬性對應即可完成參數傳遞
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){System.out.println("pojo參數傳遞 user ==> "+user);return "{'module':'pojo param'}";
}

注意:

請求參數key的名稱要和POJO中屬性的名稱一致,否則無法封裝

3.3.4 數組類型參數
  • 數組參數:請求參數名與形參對象屬性名相同且請求參數為多個,定義數組類型即可接收參數

發送請求和參數:

img

后臺接收參數:

  //數組參數:同名請求參數可以直接映射到對應名稱的形參數組對象中@RequestMapping("/arrayParam")@ResponseBodypublic String arrayParam(String[] likes){System.out.println("數組參數傳遞 likes ==> "+ Arrays.toString(likes));return "{'module':'array param'}";}
3.3.5 集合類型參數

發送請求和參數:

img

后端接收參數:

使用@RequestParam注解

//集合參數:同名請求參數可以使用@RequestParam注解映射到對應名稱的集合對象中作為數據
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){System.out.println("集合參數傳遞 likes ==> "+ likes);return "{'module':'list param'}";
}

如果不加@RequestParam運行會報錯,

img

錯誤的原因是:SpringMVC將List看做是一個POJO對象來處理,將其創建一個對象并準備把前端的數據封裝到對象中,但是List是一個接口無法創建對象,所以報錯。

  • 集合保存普通參數:請求參數名與形參集合對象名相同且請求參數為多個,@RequestParam綁定參數關系

  • 對于簡單數據類型使用數組會比集合更簡單些。

知識點1:@RequestParam
名稱@RequestParam
類型形參注解
位置SpringMVC控制器方法形參定義前面
作用綁定請求參數與處理器方法形參間的關系
相關參數required:是否為必傳參數 defaultValue:參數默認值

3.4 JSON數據傳輸參數

現在比較流行的開發方式為異步調用。前后臺以異步方式進行交換,傳輸的數據使用的是JSON,

對于JSON數據類型,我們常見的有三種:

  • json普通數組([“value1”,“value2”,“value3”,…])

  • json對象({key1:value1,key2:value2,…})

  • json對象數組([{key1:value1,…},{key2:value2,…}])

對于上述數據,前端如何發送,后端如何接收?

3.4.1 JSON普通數組

步驟:

  1. 添加jackson依賴(用來處理json的轉換)

  2. postman發送JSON數據

img

  1. 開啟SpringMVC注解支持@EnableWebMvc,其中一個功能就是支持將JSON轉換成對象
@Configuration
@ComponentScan("com.itheima.controller")
//開啟json數據類型自動轉換
@EnableWebMvc
public class SpringMvcConfig {
}
  1. 參數前添加@RequsetBody
//使用@RequestBody注解將外部傳遞的json數組數據映射到形參的集合對象中作為數據
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){System.out.println("list common(json)參數傳遞 list ==> "+likes);return "{'module':'list common for json param'}";
}
3.4.2 JSON對象數據

請求和數據的發送:

img

后端接收數據:

@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){System.out.println("pojo(json)參數傳遞 user ==> "+user);return "{'module':'pojo for json param'}";
}
3.4.3 JSON對象數組

請求和數據的發送:

img

后端接收數據:

@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){System.out.println("list pojo(json)參數傳遞 list ==> "+list);return "{'module':'list pojo for json param'}";
}

小結

SpringMVC接收JSON數據的實現步驟為:

(1)導入jackson包

(2)使用PostMan發送JSON數據

(3)開啟SpringMVC注解驅動,在配置類上添加@EnableWebMvc注解

(4)Controller方法的參數前添加@RequestBody注解

知識點1:@EnableWebMvc
名稱@EnableWebMvc
類型配置類注解
位置SpringMVC配置類定義上方
作用開啟SpringMVC多項輔助功能(轉換JSON數據等)
知識點2:@RequestBody
名稱@RequestBody
類型形參注解
位置SpringMVC控制器方法形參定義前面
作用將請求中請求體所包含的數據傳遞給請求參數,此注解一個處理器方法只能使用一次
@RequestBody與@RequestParam區別
  • 區別

    • @RequestParam用于接收url地址傳參,表單傳參【application/x-www-form-urlencoded】(主要是針對形參名與請求參數名不一致,以及傳遞集合的情況)
    • @RequestBody用于接收json數據【application/json】
  • 應用

    • 后期開發中,發送json格式數據為主,@RequestBody應用較廣
    • 如果發送非json格式數據,選用@RequestParam接收請求參數

3.5 日期類型參數傳遞

對于日期的格式有N多中輸入方式,比如:

  • 2088-08-18

  • 2088/08/18

  • 08/18/2088

前端發送請求和參數:

img

后端接收日期類型的參數:

使用@DateTimeFormat

@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2)System.out.println("參數傳遞 date ==> "+date);System.out.println("參數傳遞 date1(yyyy-MM-dd) ==> "+date1);System.out.println("參數傳遞 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);return "{'module':'data param'}";
}
知識點1:@DateTimeFormat
名稱@DateTimeFormat
類型形參注解
位置SpringMVC控制器方法形參前面
作用設定日期時間型數據格式
相關屬性pattern:指定日期時間格式字符串

3.6 響應

對于響應,主要就包含兩部分內容:

  • 響應頁面

  • 響應數據

    • 文本數據
    • json數據
3.6.1 響應頁面[了解]
@Controller
public class UserController {@RequestMapping("/toJumpPage")//注意//1.此處不能添加@ResponseBody,如果加了該注入,會直接將page.jsp當字符串返回前端//2.方法需要返回Stringpublic String toJumpPage(){System.out.println("跳轉頁面");//此處應該是轉發操作return "page.jsp";}}
3.6.2 返回文本數據[了解]
@Controller
public class UserController {@RequestMapping("/toText")//注意此處該注解就不能省略,如果省略了,會把response text當前頁面名稱去查找,如果沒有回報404錯誤@ResponseBodypublic String toText(){System.out.println("返回純文本數據");return "response text";}}
3.6.3 響應JSON數據

響應POJO對象

@Controller
public class UserController {@RequestMapping("/toJsonPOJO")@ResponseBodypublic User toJsonPOJO(){System.out.println("返回json對象數據");User user = new User();user.setName("itcast");user.setAge(15);return user;}}

返回值為實體類對象,設置返回值為實體類類型,即可實現返回對應對象的json數據,需要依賴==@ResponseBody注解和@EnableWebMvc==注解

響應POJO集合對象

@Controller
public class UserController {@RequestMapping("/toJsonList")@ResponseBodypublic List<User> toJsonList(){System.out.println("返回json集合數據");User user1 = new User();user1.setName("傳智播客");user1.setAge(15);User user2 = new User();user2.setName("黑馬程序員");user2.setAge(12);List<User> userList = new ArrayList<User>();userList.add(user1);userList.add(user2);return userList;}}
知識點1:@ResponseBody
名稱@ResponseBody
類型方法\類注解
位置SpringMVC控制器方法定義上方和控制類上
作用設置當前控制器返回值作為響應體, 寫在類上,該類的所有方法都有該注解功能
相關屬性pattern:指定日期時間格式字符串
  • 當方法上有@ReponseBody注解后

    • 方法的返回值為字符串,會將其作為文本內容直接響應給前端
    • 方法的返回值為對象,會將對象轉換成JSON響應給前端

4.Rest風格

對于Rest風格,我們需要學習的內容包括:

  • REST簡介

  • REST入門案例

  • REST快速開發

  • 案例:基于RESTful頁面數據交互

4.1REST簡介

  • REST(Representational State Transfer),表現形式狀態轉換,它是一種軟件架構風格
    當我們想表示一個網絡資源的時候,可以使用兩種方式:

    • 傳統風格資源描述形式
      • http://localhost/user/getById?id=1 查詢id為1的用戶信息
      • http://localhost/user/saveUser 保存用戶信息
    • REST風格描述形式
      • http://localhost/user/1
      • http://localhost/user

REST的優點有:

  • 隱藏資源的訪問行為,無法通過地址得知對資源是何種操作

  • 書寫簡化

但是一個相同的url地址即可以是新增也可以是修改或者查詢,那么到底我們該如何區分該請求到底是什么操作呢?

  • 按照REST風格訪問資源時使用行為動作區分對資源進行了何種操作

    • http://localhost/users 查詢全部用戶信息 GET(查詢)
    • http://localhost/users/1 查詢指定用戶信息 GET(查詢)
    • http://localhost/users 添加用戶信息 POST(新增/保存)
    • http://localhost/users 修改用戶信息 PUT(修改/更新)
    • http://localhost/users/1 刪除用戶信息 DELETE(刪除)

PS:根據REST風格對資源(后臺服務)進行訪問稱為RESTful

4.2 RestFul入門案例

1.創建web的maven項目,導入坐標

2.創建servlet,springmvc配置類

3.編寫pojo類Book

4.編寫BookController控制器類

部分代碼如下:

save方法:

@RequestMapping(value = "/users",method = RequestMethod.POST)@ResponseBodypublic String save(@RequestBody Book book){System.out.println("book save..." + book);return "{'module':'book save'}";}

delete方法:
后端獲取參數,需要做如下修改:
● 修改@RequestMapping的value屬性,將其中修改為/users/{id},目的是和路徑匹配
● 在方法的形參前添加@PathVariable注解

@Controller
public class UserController {//設置當前請求方法為DELETE,表示REST風格中的刪除操作@RequestMapping(value = "/users/{id}/{name}",method = RequestMethod.DELETE)@ResponseBodypublic String delete(@PathVariable Integer id,@PathVariable String name) {System.out.println("user delete..." + id+","+name);return "{'module':'user delete'}";}
}

小結

RESTful入門案例,我們需要學習的內容如下:

(1)設定Http請求動作(動詞)

@RequestMapping(value=“”,method = RequestMethod.POST|GET|PUT|DELETE)

(2)設定請求參數(路徑變量)

@RequestMapping(value=“/users/{id}”,method = RequestMethod.DELETE)

@ReponseBody

public String delete(@PathVariable Integer id){

}

知識點1:@PathVariable
名稱@PathVariable
類型形參注解
位置SpringMVC控制器方法形參定義前面
作用綁定路徑參數與處理器方法形參間的關系,要求路徑參數名與形參名一一對應
接收參數的注解@RequestBody、@RequestParam、@PathVariable

關于接收參數,我們學過三個注解@RequestBody@RequestParam@PathVariable,這三個注解之間的區別和應用分別是什么?

  • 區別

    • @RequestParam用于接收url地址傳參或表單傳參
    • @RequestBody用于接收json數據(請求體)
    • @PathVariable用于接收路徑參數,使用 {參數名稱} 描述路徑參數
  • 應用

    • 后期開發中,發送請求參數超過1個時,以json格式為主,@RequestBody應用較廣
    • 如果發送非json格式數據,選用@RequestParam接收請求參數
    • 采用RESTful進行開發,當參數數量較少時,例如1個,可以采用@PathVariable接收請求路徑變量,通常用于傳遞id值

4.3RestFul快速開發

知識點1:@RestController
名稱@RestController
類型類注解
位置基于SpringMVC的RESTful開發控制器類定義上方
作用設置當前控制器類為RESTful風格, 等同于@Controller與@ResponseBody兩個注解組合功能
知識點2:@GetMapping @PostMapping @PutMapping @DeleteMapping
名稱@GetMapping @PostMapping @PutMapping @DeleteMapping
類型方法注解
位置基于SpringMVC的RESTful開發控制器方法定義上方
作用設置當前控制器方法請求訪問路徑與請求動作,每種對應一個請求動作, 例如@GetMapping對應GET請求
相關屬性value(默認):請求訪問路徑

4.4RestFul實例

后端:

編寫Controller類并使用RESTful進行配置:

@RestController
@RequestMapping("/books")
public class BookController {@PostMappingpublic String save(@RequestBody Book book){System.out.println("book save ==> "+ book);return "{'module':'book save success'}";}@GetMappingpublic List<Book> getAll(){System.out.println("book getAll is running ...");List<Book> bookList = new ArrayList<Book>();Book book1 = new Book();book1.setType("計算機");book1.setName("SpringMVC入門教程");book1.setDescription("小試牛刀");bookList.add(book1);Book book2 = new Book();book2.setType("計算機");book2.setName("SpringMVC實戰教程");book2.setDescription("一代宗師");bookList.add(book2);Book book3 = new Book();book3.setType("計算機叢書");book3.setName("SpringMVC實戰教程進階");book3.setDescription("一代宗師嘔心創作");bookList.add(book3);return bookList;}
}

前端:

想要訪問靜態資源時,由于SpringMVC攔截靜態資源,導致無法訪問:

img

(1)出現錯誤的原因?

img

SpringMVC攔截了靜態資源,根據/pages/books.html去controller找對應的方法,找不到所以會報404的錯誤。

(2)SpringMVC為什么會攔截靜態資源呢?

img

(3)解決方案?

  • SpringMVC需要將靜態資源進行放行。
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {//設置靜態資源訪問過濾,當前類需要設置為配置類,并被掃描加載@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {//當訪問/pages/????時候,從/pages目錄下查找內容registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}
}
  • 該配置類是在config目錄下,SpringMVC掃描的是controller包,所以該配置類還未生效,要想生效需要將SpringMvcConfig配置類進行修改
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig {
}或者@Configuration
@ComponentScan("com.itheima")
@EnableWebMvc
public class SpringMvcConfig {
}

最后由html文件發送ajax異步請求調用controller的各個方法即可

                //添加saveBook () {axios.post("/books",this.formData).then((res)=>{});},//主頁列表查詢getAll() {axios.get("/books").then((res)=>{this.dataList = res.data;});}

5.SSM整合

前面我們已經把MybatisSpringSpringMVC三個框架進行了學習,今天主要的內容就是把這三個框架整合在一起完成我們的業務功能開發,具體如何來整合,我們一步步來學習。

流程分析:

5.1 創建工程

  • 創建一個Maven的web工程

  • pom.xml添加SSM需要的依賴jar包

    <?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>com.itheima</groupId><artifactId>springmvc_08_ssm</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins></build>
    </project>
    
  • 編寫Web項目的入口配置類ServletConfig,實現AbstractAnnotationConfigDispatcherServletInitializer重寫以下方法

    • getRootConfigClasses() :返回Spring的配置類->需要SpringConfig配置類
    • getServletConfigClasses() :返回SpringMVC的配置類->需要SpringMvcConfig配置類
    • getServletMappings() : 設置SpringMVC請求攔截路徑規則
    • getServletFilters() :設置過濾器,解決POST請求中文亂碼問題

image-20240302111300360

5.2 SSM整合[重點是各個配置的編寫]

  • SpringConfig

    • 標識該類為配置類 @Configuration
    • 掃描Service所在的包 @ComponentScan
    • 在Service層要管理事務 @EnableTransactionManagement
    • 讀取外部的properties配置文件 @PropertySource
    • 整合Mybatis需要引入Mybatis相關配置類 @Import

      image-20240302111349766

      • 第三方數據源配置類 JdbcConfig
        • 構建DataSource數據源,DruidDataSouroce,需要注入數據庫連接四要素, @Bean @Value
        • 構建平臺事務管理器,DataSourceTransactionManager,@Bean

          image-20240302111430115

      • Mybatis配置類 MybatisConfig
        • 構建SqlSessionFactoryBean并設置別名掃描與數據源,@Bean
        • 構建MapperScannerConfigurer并設置DAO層的包掃描 ,創建mapper接口的自動代理對象,@Bean

          image-20240302111450815

  • SpringMvcConfig

    • 標識該類為配置類 @Configuration
    • 掃描Controller所在的包 @ComponentScan
    • 開啟SpringMVC注解支持 @EnableWebMvc ,開啟如轉換json數據等多種輔助功能

image-20240302111509056

5.3 功能模塊[與具體的業務模塊有關]

  • 創建數據庫表

  • 根據數據庫表創建對應的模型類pojo

  • 通過Dao層完成數據庫表的增刪改查(接口+自動代理)

    image-20240302111538777

  • 編寫Service層[Service接口+實現類]

    • @Service
    • @Transactional
  • image-20240302111614355

  • ? image-20240302111732889

    • 整合Junit對業務層進行單元測試
      • @RunWith
      • @ContextConfiguration
      • @Test
    • image-20240302111642158

  • 編寫Controller層

    • 接收請求 @RequestMapping @GetMapping @PostMapping @PutMapping @DeleteMapping
    • 接收數據 簡單、POJO、嵌套POJO、集合、數組、JSON數據類型
      • @RequestParam
      • @PathVariable
      • @RequestBody
    • 轉發業務層
      • @Autowired
    • 響應結果
      • @ResponseBody

image-20240302111758259

PS:SSM整合完之后就可以進行junit測試service類以及postman測試controller類

6.統一結果封裝

SSM整合以及功能模塊開發完成后,接下來,我們在上述案例的基礎上分析下有哪些問題需要我們去解決下。首先第一個問題是:

隨著業務的增長,我們需要返回的數據類型會越來越多。對于前端開發人員在解析數據的時候就比較凌亂了,所以對于前端來說,如果后臺能夠返回一個統一的數據結果,前端在解析的時候就可以按照一種方式進行解析。開發就會變得更加簡單。

所以我們就想能不能將返回結果的數據進行統一,具體如何來做,大體的思路為:

  • 為了封裝返回的結果數據:創建結果模型類,封裝數據到data屬性中

  • 為了封裝返回的數據是何種操作及是否操作成功:封裝操作結果到code屬性中

  • 操作失敗后為了封裝返回的錯誤信息:封裝特殊消息到message(msg)屬性中

img

根據分析,我們可以設置統一數據返回結果類

1.Result:

public class Result {//描述統一格式中的數據private Object data;//描述統一格式中的編碼,用于區分操作,可以簡化配置0或1表示成功失敗private Integer code;//描述統一格式中的消息,可選屬性private String msg;public Result() {}//構造方法是方便對象的創建public Result(Integer code,Object data) {this.data = data;this.code = code;}//構造方法是方便對象的創建public Result(Integer code, Object data, String msg) {this.data = data;this.code = code;this.msg = msg;}//setter...getter...省略
}

**注意:**Result類名及類中的字段并不是固定的,可以根據需要自行增減提供若干個構造方法,方便操作。

2.Code:

//狀態碼
public class Code {public static final Integer SAVE_OK = 20011;public static final Integer DELETE_OK = 20021;public static final Integer UPDATE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERR = 20010;public static final Integer DELETE_ERR = 20020;public static final Integer UPDATE_ERR = 20030;public static final Integer GET_ERR = 20040;
}

**注意:**code類中的常量設計也不是固定的,可以根據需要自行增減,例如將查詢再進行細分為GET_OK,GET_ALL_OK,GET_PAGE_OK等。

3.修改Controller類的返回值為Result

//統一每一個控制器方法返回值
@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService bookService;@PostMappingpublic Result save(@RequestBody Book book) {boolean flag = bookService.save(book);return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);}@PutMappingpublic Result update(@RequestBody Book book) {boolean flag = bookService.update(book);return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);}@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id) {boolean flag = bookService.delete(id);return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);}@GetMapping("/{id}")public Result getById(@PathVariable Integer id) {Book book = bookService.getById(id);Integer code = book != null ? Code.GET_OK : Code.GET_ERR;String msg = book != null ? "" : "數據查詢失敗,請重試!";return new Result(code,book,msg);}@GetMappingpublic Result getAll() {List<Book> bookList = bookService.getAll();Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;String msg = bookList != null ? "" : "數據查詢失敗,請重試!";return new Result(code,bookList,msg);}
}

至此,我們的返回結果就已經能以一種統一的格式返回給前端。前端根據返回的結果,先從中獲取code,根據code判斷,如果成功則取data屬性的值,如果失敗,則取msg中的值做提示。

7.統一異常處理

先來看下異常的種類及出現異常的原因:

  • 框架內部拋出的異常:因使用不合規導致

  • 數據層拋出的異常:因外部服務器故障導致(例如:服務器訪問超時)

  • 業務層拋出的異常:因業務邏輯書寫錯誤導致(例如:遍歷業務書寫操作,導致索引異常等)

  • 表現層拋出的異常:因數據收集、校驗等規則導致(例如:不匹配的數據類型間導致異常)

  • 工具類拋出的異常:因工具類書寫不嚴謹不夠健壯導致(例如:必要釋放的連接長期未釋放等)

思考

  1. 各個層級均出現異常,異常處理代碼書寫在哪一層?
    所有的異常均拋出到表現層進行處理

  2. 異常的種類很多,表現層如何將所有的異常都處理到呢?
    異常分類

  3. 表現層處理異常,每個方法中單獨書寫,代碼書寫量巨大且意義不強,如何解決?
    AOP

7.1異常處理器

對于上面這些問題及解決方案,SpringMVC已經為我們提供了一套解決方案:

  • 異常處理器:

    • 集中的、統一的處理項目中出現的異常,并返回數據給前端

      外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

知識點1:@RestControllerAdvice
名稱@RestControllerAdvice
類型類注解
位置Rest風格開發的控制器增強類定義上方
作用為Rest風格開發的控制器類做增強
知識點2:@ExceptionHandler
名稱@ExceptionHandler
類型方法注解
位置專用于異常處理的控制器方法上方
作用設置指定異常的處理方案,功能等同于控制器方法, 出現異常后終止原始控制器執行,并轉入當前方法執行

**說明:**此類方法可以根據處理的異常不同,制作多個方法分別處理對應的異常

7.2項目異常處理方案

7.2.1異常分類

異常處理器我們已經能夠使用了,那么在咱們的項目中該如何來處理異常呢?

因為異常的種類有很多,如果每一個異常都對應一個@ExceptionHandler,那得寫多少個方法來處理各自的異常,所以我們在處理異常之前,需要對異常進行一個分類:

  • 業務異常(BusinessException)

    • 規范的用戶行為產生的異常
      • 用戶在頁面輸入內容的時候未按照指定格式進行數據填寫,如在年齡框輸入的是字符串
        img
    • 不規范的用戶行為操作產生的異常
      • 如用戶故意傳遞錯誤數據
        img
  • 系統異常(SystemException)

    • 項目運行過程中可預計但無法避免的異常
      • 比如數據庫或服務器宕機
  • 其他異常(Exception)

    • 編程人員未預期到的異常,如:用到的文件不存在
      img

將異常分類以后,針對不同類型的異常,要提供具體的解決方案:

7.2.2異常解決方案
  • 業務異常(BusinessException)

    • 發送對應消息傳遞給用戶,提醒規范操作
      • 大家常見的就是提示用戶名已存在或密碼格式不正確等
  • 系統異常(SystemException)

    • 發送固定消息傳遞給用戶,安撫用戶
      • 系統繁忙,請稍后再試
      • 系統正在維護升級,請稍后再試
      • 系統出問題,請聯系系統管理員等
    • 發送特定消息給運維人員,提醒維護
      • 可以發送短信、郵箱或者是公司內部通信軟件
    • 記錄日志
      • 發消息和記錄日志對用戶來說是不可見的,屬于后臺程序
  • 其他異常(Exception)

    • 發送固定消息傳遞給用戶,安撫用戶
    • 發送特定消息給編程人員,提醒維護(納入預期范圍內)
      • 一般是程序沒有考慮全,比如未做非空校驗等
    • 記錄日志
7.2.3異常解決方案的具體表現

步驟1:自定義異常類

//自定義異常處理器,用于封裝異常信息,對異常進行分類
public class SystemException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public SystemException(Integer code, String message) {super(message);this.code = code;}public SystemException(Integer code, String message, Throwable cause) {super(message, cause);this.code = code;}}//自定義異常處理器,用于封裝異常信息,對異常進行分類
public class BusinessException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public BusinessException(Integer code, String message) {super(message);this.code = code;}public BusinessException(Integer code, String message, Throwable cause) {super(message, cause);this.code = code;}}

說明:

  • 讓自定義異常類繼承RuntimeException的好處是,后期在拋出這兩個異常的時候,就不用在try…catch…或throws了

  • 自定義異常類中添加code屬性的原因是為了更好的區分異常是來自哪個業務的

步驟2:將其他異常包成自定義異常

假如在BookServiceImpl的getById方法拋異常了,該如何來包裝呢?

public Book getById(Integer id) {//模擬業務異常,包裝成自定義異常if(id == 1){throw new BusinessException(Code.BUSINESS_ERR,"請不要使用你的技術挑戰我的耐性!");}//模擬系統異常,將可能出現的異常進行包裝,轉換成自定義異常try{int i = 1/0;}catch (Exception e){throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服務器訪問超時,請重試!",e);}return bookDao.getById(id);
}

具體的包裝方式有:

  • 方式一:try{}catch(){}在catch中重新throw我們自定義異常即可。

  • 方式二:直接throw自定義異常即可

上面為了使code看著更專業些,我們在Code類中再新增需要的屬性

//狀態碼
public class Code {public static final Integer SAVE_OK = 20011;public static final Integer DELETE_OK = 20021;public static final Integer UPDATE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERR = 20010;public static final Integer DELETE_ERR = 20020;public static final Integer UPDATE_ERR = 20030;public static final Integer GET_ERR = 20040;public static final Integer SYSTEM_ERR = 50001;public static final Integer SYSTEM_TIMEOUT_ERR = 50002;public static final Integer SYSTEM_UNKNOW_ERR = 59999;public static final Integer BUSINESS_ERR = 60002;
}

步驟3:處理器類中處理自定義異常

//@RestControllerAdvice用于標識當前類為REST風格對應的異常處理器
@RestControllerAdvice
public class ProjectExceptionAdvice {//@ExceptionHandler用于設置當前處理器類對應的異常類型@ExceptionHandler(SystemException.class)public Result doSystemException(SystemException ex){//記錄日志//發送消息給運維//發送郵件給開發人員,ex對象發送給開發人員return new Result(ex.getCode(),null,ex.getMessage());}@ExceptionHandler(BusinessException.class)public Result doBusinessException(BusinessException ex){return new Result(ex.getCode(),null,ex.getMessage());}//除了自定義的異常處理器,保留對Exception類型的異常處理,用于處理非預期的異常@ExceptionHandler(Exception.class)public Result doOtherException(Exception ex){//記錄日志//發送消息給運維//發送郵件給開發人員,ex對象發送給開發人員return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系統繁忙,請稍后再試!");}
}

8.前后臺協議協調

項目結構:

image-20240303145801808

<!DOCTYPE html><html><head><!-- 頁面meta --><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>SpringMVC案例</title><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"><!-- 引入樣式 --><link rel="stylesheet" href="../plugins/elementui/index.css"><link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css"><link rel="stylesheet" href="../css/style.css"></head><body class="hold-transition"><div id="app"><div class="content-header"><h1>圖書管理</h1></div><div class="app-container"><div class="box"><div class="filter-container"><el-input placeholder="圖書名稱" v-model="pagination.queryString" style="width: 200px;" class="filter-item"></el-input><el-button @click="getAll()" class="dalfBut">查詢</el-button><el-button type="primary" class="butT" @click="handleCreate()">新建</el-button></div><el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row><el-table-column type="index" align="center" label="序號"></el-table-column><el-table-column prop="type" label="圖書類別" align="center"></el-table-column><el-table-column prop="name" label="圖書名稱" align="center"></el-table-column><el-table-column prop="description" label="描述" align="center"></el-table-column><el-table-column label="操作" align="center"><template slot-scope="scope"><el-button type="primary" size="mini" @click="handleUpdate(scope.row)">編輯</el-button><el-button type="danger" size="mini" @click="handleDelete(scope.row)">刪除</el-button></template></el-table-column></el-table><!-- 新增標簽彈層 --><div class="add-form"><el-dialog title="新增圖書" :visible.sync="dialogFormVisible"><el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px"><el-row><el-col :span="12"><el-form-item label="圖書類別" prop="type"><el-input v-model="formData.type"/></el-form-item></el-col><el-col :span="12"><el-form-item label="圖書名稱" prop="name"><el-input v-model="formData.name"/></el-form-item></el-col></el-row><el-row><el-col :span="24"><el-form-item label="描述"><el-input v-model="formData.description" type="textarea"></el-input></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取消</el-button><el-button type="primary" @click="handleAdd()">確定</el-button></div></el-dialog></div><!-- 編輯標簽彈層 --><div class="add-form"><el-dialog title="編輯檢查項" :visible.sync="dialogFormVisible4Edit"><el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px"><el-row><el-col :span="12"><el-form-item label="圖書類別" prop="type"><el-input v-model="formData.type"/></el-form-item></el-col><el-col :span="12"><el-form-item label="圖書名稱" prop="name"><el-input v-model="formData.name"/></el-form-item></el-col></el-row><el-row><el-col :span="24"><el-form-item label="描述"><el-input v-model="formData.description" type="textarea"></el-input></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible4Edit = false">取消</el-button><el-button type="primary" @click="handleEdit()">確定</el-button></div></el-dialog></div></div></div></div></body><!-- 引入組件庫 --> <script src="../js/vue.js"></script><script src="../plugins/elementui/index.js"></script><script type="text/javascript" src="../js/jquery.min.js"></script><script src="../js/axios-0.18.0.js"></script><!-- 重點!!!!!!--><script>var vue = new Vue({el: '#app',data:{pagination: {},dataList: [],//當前頁要展示的列表數據formData: {},//表單數據dialogFormVisible: false,//控制表單是否可見dialogFormVisible4Edit:false,//編輯表單是否可見rules: {//校驗規則type: [{ required: true, message: '圖書類別為必填項', trigger: 'blur' }],name: [{ required: true, message: '圖書名稱為必填項', trigger: 'blur' }]}},//鉤子函數,VUE對象初始化完成后自動執行created() {this.getAll();},methods: {//列表getAll() {//發送ajax請求axios.get("/books").then((res)=>{this.dataList = res.data.data;});},//彈出添加窗口handleCreate() {this.dialogFormVisible = true;this.resetForm();},//重置表單resetForm() {this.formData = {};},//添加handleAdd () {//發送ajax請求axios.post("/books",this.formData).then((res)=>{console.log(res.data);//如果操作成功,關閉彈層,顯示數據if(res.data.code == 20011){this.dialogFormVisible = false;this.$message.success("添加成功");}else if(res.data.code == 20010){this.$message.error("添加失敗");}else{//服務器異常等this.$message.error(res.data.msg);}}).finally(()=>{this.getAll();});},//彈出編輯窗口handleUpdate(row) {// console.log(row);   //row.id 查詢條件//查詢數據,根據id查詢axios.get("/books/"+row.id).then((res)=>{// console.log(res.data.data);if(res.data.code == 20041){//展示彈層,加載數據this.formData = res.data.data;this.dialogFormVisible4Edit = true;}else{this.$message.error(res.data.msg);}});},//編輯handleEdit() {//發送ajax請求axios.put("/books",this.formData).then((res)=>{//如果操作成功,關閉彈層,顯示數據if(res.data.code == 20031){this.dialogFormVisible4Edit = false;this.$message.success("修改成功");}else if(res.data.code == 20030){this.$message.error("修改失敗");}else{this.$message.error(res.data.msg);}}).finally(()=>{this.getAll();});},// 刪除handleDelete(row) {//1.彈出提示框this.$confirm("此操作永久刪除當前數據,是否繼續?","提示",{type:'info'}).then(()=>{//2.做刪除業務axios.delete("/books/"+row.id).then((res)=>{if(res.data.code == 20021){this.$message.success("刪除成功");}else{this.$message.error("刪除失敗");}}).finally(()=>{this.getAll();});}).catch(()=>{//3.取消刪除this.$message.info("取消刪除操作");});}}})</script></html>

9.攔截器interceptor

瀏覽器訪問資源路徑流程:

img

(1)瀏覽器發送一個請求會先到Tomcat的web服務器

(2)Tomcat服務器接收到請求以后,會去判斷請求的是靜態資源還是動態資源

(3)如果是靜態資源,會直接到Tomcat的項目部署目錄下去直接訪問

(4)如果是動態資源,就需要交給項目的后臺代碼進行處理

(5)在找到具體的方法之前,我們可以去配置過濾器(可以配置多個),按照順序進行執行

(6)然后進入到到中央處理器(SpringMVC中的內容),SpringMVC會根據配置的規則進行攔截

(7)如果滿足規則,則進行處理,找到其對應的controller類中的方法進行執行,完成后返回結果

(8)如果不滿足規則,則不進行處理

(9)這個時候,如果我們需要在每個Controller方法執行的前后添加業務,具體該如何來實現?

這個就是攔截器要做的事。

9.1 攔截器概念

  • 攔截器(Interceptor)是一種動態攔截方法調用的機制,在SpringMVC中動態攔截控制器方法的執行

  • 作用:

    • 在指定的方法調用前后執行預先設定的代碼
    • 阻止原始方法的執行
    • 總結:攔截器就是用來做增強(底層就是AOP)

攔截器和過濾器之間的區別是什么?

  • 歸屬不同:Filter屬于Servlet技術,Interceptor屬于SpringMVC技術

  • 攔截內容不同:Filter對所有訪問進行增強,Interceptor僅針對SpringMVC的訪問進行增強

img

9.2 攔截器入門案例

項目結構:

img

1.創建攔截器類

@Component
//定義攔截器類,實現HandlerInterceptor接口
//注意當前類必須受Spring容器控制
public class ProjectInterceptor 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...");}
}

**注意:**攔截器類要被SpringMVC容器掃描到。

2.配置攔截器類

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");}@Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置攔截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*" );}
}

**注意:**此處也可以簡化SpringMvcSupport的編寫,直接讓SpringMvcConfig實現WebMvcConfigurer接口即可,此后就不用再寫SpringMvcSupport類了

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//實現WebMvcConfigurer接口可以簡化開發,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多攔截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}

攔截器執行流程

img

ps:handle[controller的方法]

9.3 攔截器參數

preHandle

原始方法之前運行

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod hm = (HandlerMethod)handler;String methodName = hm.getMethod().getName();//可以獲取方法的名稱System.out.println("preHandle..."+methodName);return true;
}
  • request:請求對象

  • response:響應對象

  • handler:被調用的處理器對象,本質上是一個方法對象[HandleMethod],對反射中的Method對象進行了再包裝

postHandle

原始方法運行后運行,如果原始方法被攔截,則不執行

public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle");
}

前三個參數和上面的是一致的。

modelAndView:如果處理器執行完成具有返回結果,可以讀取到對應數據與頁面信息,并進行調整

因為咱們現在都是返回json數據,所以該參數的使用率不高。

afterCompletion

攔截器最后執行的方法,無論原始方法是否執行

public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {System.out.println("afterCompletion");
}

前三個參數與上面的是一致的。

ex:如果處理器執行過程中出現異常對象,可以針對異常情況進行單獨處理

因為我們現在已經有全局異常處理器類,所以該參數的使用率也不高。

這三個方法中,最常用的是preHandle,在這個方法中可以通過返回值來決定是否要進行放行,我們可以把業務邏輯放在該方法中,如果滿足業務則返回true放行,不滿足則返回false攔截。

9.4 攔截器鏈配置

img

preHandle:與配置順序相同,必定運行

postHandle:與配置順序相反,可能不運行

afterCompletion:與配置順序相反,可能不運行。

這個順序不太好記,最終只需要把握住一個原則即可:以最終的運行結果為準

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717949.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717949.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717949.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

易語言源代碼5000例

僅供學習研究交流使用 加群下載

探索MyBatis-Plus的高階用法

引言 MyBatis-Plus 是 MyBatis 的增強工具包&#xff0c;提供了許多方便快捷的功能來簡化開發&#xff0c;提高效率。除了基本的 CRUD 操作外&#xff0c;MyBatis-Plus 還提供了一些高級功能&#xff0c;本文將探討 MyBatis-Plus 的高階用法&#xff0c;幫助開發者更好地利用該…

Linux服務器搭建超簡易跳板機連接阿里云服務器

簡介 想要規范內部連接阿里云云服務器的方式&#xff0c;但是最近懶病犯了&#xff0c;先搞一個簡易式的跳板機過渡一下&#xff0c;順便在出一個教程&#xff0c;其他以后再說&#xff01; 配置方法 創建密鑰 登錄阿里云&#xff0c;找到云服務器ECS控制臺&#xff0c;點擊…

【小白友好】LeetCode 打家劫舍 III

https://leetcode.cn/problems/house-robber-iii/description/ 前言 建議還是先看看動態規劃的基礎題再看這個。動態規劃是不刷題&#xff0c;自己100%想不出來的。 基礎題&#xff1a; 23 小白想法 現在我們想遍歷的數據結構不是數組了&#xff0c;而是一顆樹。在樹上的d…

C++遞推

統計每個月兔子的總數 #include<bits/stdc.h> using namespace std; int n,sum0; void f(int); int main() {int a[1000];cin>>n;a[1]1;a[2]2;for(int i3;i<1000;i){a[i]a[i-1]a[i-2];}cout<<a[n];return 0; } void f(int n){}猴子吃桃子 #include<b…

2024年華為OD機試真題-電腦病毒感染-Python-OD統一考試(C卷)

題目描述: 一個局域網內有很多臺電腦,分別標注為0 - N-1的數字。相連接的電腦距離不一樣,所以感染時間不一樣,感染時間用t表示。 其中網絡內一個電腦被病毒感染,其感染網絡內所有的電腦需要最少需要多長時間。如果最后有電腦不會感染,則返回-1 給定一個數組times表示一個…

在Spring Boot中如何實現異常處理?

在Spring Boot中&#xff0c;異常處理可以通過幾種方式實現&#xff0c;以提高應用程序的健壯性和用戶體驗。這些方法包括使用ControllerAdvice注解、ExceptionHandler注解、實現ErrorController接口等。下面是一些實現Spring Boot異常處理的常用方法&#xff1a; 1. 使用Cont…

Git實戰(2)

git work flow ------------------------------------------------------- ---------------------------------------------------------------- 場景問題及處理 問題1&#xff1a;最近提交了 a,b,c,d記錄&#xff0c;想把b記錄刪掉其他提交記錄保留&#xff1a; git reset …

【C++ 編程指南】

C 編程指南 ■ C環境安裝■ C 基本語法■ 預定義宏■ # 和 ## 運算符■ C 引用■ C 命名空間■ 定義命名空間■ using 指令■ 嵌套的命名空間 ■ String類■ 類■ 類的static靜態成員 ■ C 繼承■ 繼承類型 public、protected 或 private■ 訪問控制和繼承■ 多繼承■ 數據抽象…

機器學習-面經

經歷了2023年的秋招&#xff0c;現在也已經入職半年了&#xff0c;空閑時間將面試中可能遇到的機器學習問題整理了一下&#xff0c;可能答案也會有錯誤的&#xff0c;希望大家能指出&#xff01;另外&#xff0c;不論是實習&#xff0c;還是校招&#xff0c;都祝福大家能夠拿到…

990-28產品經理:Different types of IT risk 不同類型的IT風險

Your IT systems and the information that you hold on them face a wide range of risks. If your business relies on technology for key operations and activities, you need to be aware of the range and nature of those threats. 您的IT系統和您在其中持有的信息面臨…

數據結構c版(2)——二叉樹

本章我們來了解一下二叉樹這一概念。 目錄 1.樹概念及結構 1.1樹的概念??????? 1.2 樹的特點&#xff1a; 1.3 樹的相關概念 1.4 樹的表示??????? 1.5 樹在實際中的運用&#xff08;表示文件系統的目錄樹結構&#xff09; 2.二叉樹概念及結構 2.1概念 …

Qt 簡約美觀的動畫 擺鐘風格 第十季

&#x1f60a; 今天給大家分享一個擺鐘風格的加載動畫 &#x1f60a; 效果如下: 最近工作忙起來了 , 后續再分享其他有趣的加載動畫吧. 一共三個文件 , 可以直接編譯運行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <Q…

【C++】用文件流的put和get成員函數讀寫文件

題目 編寫一個mycopy程序&#xff0c;實現文件復制的功能。用法是在控制臺輸入&#xff1a; mycooy 源文件名 目標文件名 參數介紹 m a i n main main 函數的參數有兩個&#xff0c;一個int類型參數和一個指針數組。 a r g c argc argc 表示參數的個數。參數為void時 a r g …

機器人 標準DH與改進DH

文章目錄 1 建立機器人坐標系1.1 連桿編號1.2 關節編號1.3 坐標系方向2 標準DH(STD)2.1 確定X軸方向2.2 建模步驟2.3 變換順序2.4 變換矩陣3 改進DH(MDH)3.1 確定X軸方向3.2 建模步驟3.3 變換順序3.4 變換矩陣4 標準DH與改進DH區別5 Matlab示例參考鏈接1 建立機器人坐標系 1.1…

Elasticsearch:如何創建搜索引擎

作者&#xff1a;Jessica Taylor 搜索引擎是生活中我們認為理所當然的事情之一。 每當我們尋找某些東西時&#xff0c;我們都會將一個單詞或短語放入搜索引擎&#xff0c;就像魔術一樣&#xff0c;它會為我們提供一個匹配結果列表。 現在可能感覺不那么神奇了&#xff0c;因為這…

Go-知識struct

Go-知識struct 1. struct 的定義1.1 定義字段1.2 定義方法 2. struct的復用3. 方法受體4. 字段標簽4.1 Tag是Struct的一部分4.2 Tag 的約定4.3 Tag 的獲取 githupio地址&#xff1a;https://a18792721831.github.io/ 1. struct 的定義 Go 語言的struct與Java中的class類似&am…

第二十三章 :Docker 部署 Redis

第二十三章 :Docker Redis 部署 Docker version 25.0.3, build 4debf41 ,Docker Compose version v2.24.2Redis-6.0.6 鏡像 redis:6.0.6-alpineRedis-6.0.6版本 部署規劃 服務器IP192.168.92.105端口6379安裝目錄/home/work/docker-redis-6.0.6數據映射目錄/home/work/do…

最簡單的基于 FFmpeg 的收流器(以接收 RTMP 為例)

最簡單的基于 FFmpeg 的收流器&#xff08;以接收 RTMP 為例&#xff09; 最簡單的基于 FFmpeg 的收流器&#xff08;以接收 RTMP 為例&#xff09;正文結果工程文件下載參考鏈接 最簡單的基于 FFmpeg 的收流器&#xff08;以接收 RTMP 為例&#xff09; 參考雷霄驊博士的文章…

藍凌OA frpt_listreport_definefield.aspx接口存在SQL注入漏洞

免責聲明&#xff1a;文章來源互聯網收集整理&#xff0c;請勿利用文章內的相關技術從事非法測試&#xff0c;由于傳播、利用此文所提供的信息或者工具而造成的任何直接或者間接的后果及損失&#xff0c;均由使用者本人負責&#xff0c;所產生的一切不良后果與文章作者無關。該…