三、SpringMVC

三、SpringMVC

1、SpringMVC簡介

1.1、什么是MVC

MVC是一種軟件架構的思想,將軟件按照模型、視圖、控制器來劃分

M:Model,模型層,指工程中的JavaBean,作用是處理數據

JavaBean分為兩類:

  • 一類稱為實體類Bean:專門存儲業務數據的,如 Student、User 等
  • 一類稱為業務處理 Bean:指 Service 或 Dao 對象,專門用于處理業務邏輯和數據訪問。

V:View,視圖層,指工程中的html或jsp等頁面,作用是與用戶進行交互,展示數據

C:Controller,控制層,指工程中的servlet,作用是接收請求和響應瀏覽器

MVC的工作流程: 用戶通過視圖層發送請求到服務器,在服務器中請求被Controller接收,Controller

調用相應的Model層處理請求,處理完畢將結果返回到Controller,Controller再根據請求處理的結果

找到相應的View視圖,渲染數據后最終響應給瀏覽器

1.2、什么是SpringMVC

SpringMVC是Spring的一個后續產品,是Spring的一個子項目

SpringMVC 是 Spring 為表述層開發提供的一整套完備的解決方案。在表述層框架歷經 Strust、

WebWork、Strust2 等諸多產品的歷代更迭之后,目前業界普遍選擇了 SpringMVC 作為 Java EE 項目

表述層開發的首選方案

注:三層架構分為表述層(或表示層)、業務邏輯層、數據訪問層,表述層表示前臺頁面和后臺

servlet

1.3、SpringMVC的特點

  • Spring 家族原生產品,與 IOC 容器等基礎設施無縫對接
  • 基于原生的Servlet,通過了功能強大的前端控制器DispatcherServlet,對請求和響應進行統一

處理

  • 表述層各細分領域需要解決的問題全方位覆蓋,提供全面解決方案
  • 代碼清新簡潔,大幅度提升開發效率
  • 內部組件化程度高,可插拔式組件即插即用,想要什么功能配置相應組件即可
  • 性能卓著,尤其適合現代大型、超大型互聯網項目要求

2、入門案例

2.1、開發環境

IDE:idea 2019.2

構建工具:maven3.5.4

服務器:tomcat8.5

Spring版本:5.3.1

2.2、創建maven工程

①添加web模塊

②打包方式:war

③引入依賴

<dependencies><!-- SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.1</version></dependency><!-- 日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!-- ServletAPI --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- Spring5和Thymeleaf整合包 --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.12.RELEASE</version></dependency>
</dependencies>

注:由于 Maven 的傳遞性,我們不必將所有需要的包全部配置依賴,而是配置最頂端的依賴,其他靠

傳遞性導入。

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

2.3、配置web.xml

注冊SpringMVC的前端控制器DispatcherServlet

①默認配置方式

此配置作用下,SpringMVC的配置文件默認位于WEB-INF下,默認名稱為-

servlet.xml,例如,以下配置所對應SpringMVC的配置文件位于WEB-INF下,文件名為springMVC

servlet.xml

<!-- 配置SpringMVC的前端控制器,對瀏覽器發送的請求統一進行處理 -->
<servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass>
</servlet>
<servlet-mapping><servlet-name>springMVC</servlet-name><!--設置springMVC的核心控制器所能處理的請求的請求路徑/所匹配的請求可以是/login或.html或.js或.css方式的請求路徑但是/不能匹配.jsp請求路徑的請求--><url-pattern>/</url-pattern>
</servlet-mapping>

②擴展配置方式

可通過init-param標簽設置SpringMVC配置文件的位置和名稱,通過load-on-startup標簽設置

SpringMVC前端控制器DispatcherServlet的初始化時間

<!-- 配置SpringMVC的前端控制器,對瀏覽器發送的請求統一進行處理 -->
<servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass><!-- 通過初始化參數指定SpringMVC配置文件的位置和名稱 --><init-param><!-- contextConfigLocation為固定值 --><param-name>contextConfigLocation</param-name><!-- 使用classpath:表示從類路徑查找配置文件,例如maven工程中的src/main/resources --><param-value>classpath:springMVC.xml</param-value></init-param><!--作為框架的核心組件,在啟動過程中有大量的初始化操作要做而這些操作放在第一次請求時才執行會嚴重影響訪問速度因此需要通過此標簽將啟動控制DispatcherServlet的初始化時間提前到服務器啟動時--><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>springMVC</servlet-name><!--設置springMVC的核心控制器所能處理的請求的請求路徑/所匹配的請求可以是/login或.html或.js或.css方式的請求路徑但是/不能匹配.jsp請求路徑的請求--><url-pattern>/</url-pattern>
</servlet-mapping>

注:

標簽中使用/和/*的區別:

/所匹配的請求可以是/login或.html或.js或.css方式的請求路徑,但是/不能匹配.jsp請求路徑的請

因此就可以避免在訪問jsp頁面時,該請求被DispatcherServlet處理,從而找不到相應的頁面

/*則能夠匹配所有請求,例如在使用過濾器時,若需要對所有請求進行過濾,就需要使用/*的寫

2.4、創建請求控制器

由于前端控制器對瀏覽器發送的請求進行了統一的處理,但是具體的請求有不同的處理過程,因此需要創建處理具體請求的類,即請求控制器

請求控制器中每一個處理請求的方法成為控制器方法

因為SpringMVC的控制器由一個POJO(普通的Java類)擔任,因此需要通過@Controller注解將其標識為一個控制層組件,交給Spring的IoC容器管理,此時SpringMVC才能夠識別控制器的存在

@Controller
public class HelloController {
}

2.5、創建SpringMVC的配置文件

<!-- 自動掃描包 -->
<context:component-scan base-package="com.atguigu.mvc.controller"/>
<!-- 配置Thymeleaf視圖解析器 -->
<bean id="viewResolver"class="org.thymeleaf.spring5.view.ThymeleafViewResolver"><property name="order" value="1"/><property name="characterEncoding" value="UTF-8"/><property name="templateEngine"><bean class="org.thymeleaf.spring5.SpringTemplateEngine"><property name="templateResolver"><beanclass="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"><!-- 視圖前綴 --><property name="prefix" value="/WEB-INF/templates/"/><!-- 視圖后綴 --><property name="suffix" value=".html"/><property name="templateMode" value="HTML5"/><property name="characterEncoding" value="UTF-8" /></bean></property></bean></property>
</bean>
<!--處理靜態資源,例如html、js、css、jpg若只設置該標簽,則只能訪問靜態資源,其他請求則無法訪問此時必須設置<mvc:annotation-driven/>解決問題
-->
<mvc:default-servlet-handler/>
<!-- 開啟mvc注解驅動 -->
<mvc:annotation-driven><mvc:message-converters><!-- 處理響應中文內容亂碼 --><beanclass="org.springframework.http.converter.StringHttpMessageConverter"><property name="defaultCharset" value="UTF-8" /><property name="supportedMediaTypes"><list><value>text/html</value><value>application/json</value></list></property></bean></mvc:message-converters>
</mvc:annotation-driven>

2.6、測試HelloWorld

①實現對首頁的訪問

在請求控制器中創建處理請求的方法

// @RequestMapping注解:處理請求和控制器方法之間的映射關系
// @RequestMapping注解的value屬性可以通過請求地址匹配請求,/表示的當前工程的上下文路徑
// localhost:8080/springMVC/
@RequestMapping("/")
public String index() {//設置視圖名稱return "index";
}

②通過超鏈接跳轉到指定頁面

在主頁index.html中設置超鏈接

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>首頁</title></head><body><h1>首頁</h1><a th:href="@{/hello}">HelloWorld</a><br/></body>
</html>

在請求控制器中創建處理請求的方法

@RequestMapping("/hello")
public String HelloWorld() {return "target";
}

2.7、總結

瀏覽器發送請求,若請求地址符合前端控制器的url-pattern,該請求就會被前端控制器DispatcherServlet處理。前端控制器會讀取SpringMVC的核心配置文件,通過掃描組件找到控制器,

將請求地址和控制器中@RequestMapping注解的value屬性值進行匹配,若匹配成功,該注解所標識的控制器方法就是處理請求的方法。處理請求的方法需要返回一個字符串類型的視圖名稱,該視圖名稱會被視圖解析器解析,加上前綴和后綴組成視圖的路徑,通過Thymeleaf對視圖進行渲染,最終轉發到視圖所對應頁面

3、@RequestMapping注解

3.1、@RequestMapping注解的功能

從注解名稱上我們可以看到,@RequestMapping注解的作用就是將請求和處理請求的控制器方法關聯起來,建立映射關系。

SpringMVC 接收到指定的請求,就會來找到在映射關系中對應的控制器方法來處理這個請求。

3.2、@RequestMapping注解的位置

@RequestMapping標識一個類:設置映射請求的請求路徑的初始信息

@RequestMapping標識一個方法:設置映射請求請求路徑的具體信息

@Controller
@RequestMapping("/test")
public class RequestMappingController {//此時請求映射所映射的請求的請求路徑為:/test/testRequestMapping@RequestMapping("/testRequestMapping")public String testRequestMapping(){return "success";}
}

3.3、@RequestMapping注解的value屬性

@RequestMapping注解的value屬性通過請求的請求地址匹配請求映射

@RequestMapping注解的value屬性是一個字符串類型的數組,表示該請求映射能夠匹配多個請求地址所對應的請求

@RequestMapping注解的value屬性必須設置,至少通過請求地址匹配請求映射

<a th:href="@{/testRequestMapping}">測試@RequestMapping的value屬性--
>/testRequestMapping</a><br>
<a th:href="@{/test}">測試@RequestMapping的value屬性-->/test</a><br>
@RequestMapping(value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){return "success";
}

3.4、@RequestMapping注解的method屬性

@RequestMapping注解的method屬性通過請求的請求方式(get或post)匹配請求映射

post請求只有一種情況:表單提交method設置為post

@RequestMapping注解的method屬性是一個RequestMethod類型的數組,表示該請求映射能夠匹配多種請求方式的請求

若當前請求的請求地址滿足請求映射的value屬性,但是請求方式不滿足method屬性,則瀏覽器報錯

405:Request method ‘POST’ not supported

<a th:href="@{/test}">測試@RequestMapping的value屬性-->/test</a><br>
<form th:action="@{/test}" method="post"><input type="submit">
</form>
@RequestMapping(value = {"/testRequestMapping", "/test"},method = {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){return "success";
}

注:

1、對于處理指定請求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解

處理get請求的映射–>@GetMapping

處理post請求的映射–>@PostMapping

處理put請求的映射–>@PutMapping

處理delete請求的映射–>@DeleteMapping

2、常用的請求方式有get,post,put,delete

但是目前瀏覽器只支持get和post,若在form表單提交時,為method設置了其他請求方式的字符

串(put或delete),則按照默認的請求方式get處理

若要發送put和delete請求,則需要通過spring提供的過濾器HiddenHttpMethodFilter,在

RESTful部分會講到

3.5、@RequestMapping注解的params屬性(了解)

@RequestMapping注解的params屬性通過請求的請求參數匹配請求映射

@RequestMapping注解的params屬性是一個字符串類型的數組,可以通過四種表達式設置請求參數

和請求映射的匹配關系

“param”:要求請求映射所匹配的請求必須攜帶param請求參數

“!param”:要求請求映射所匹配的請求必須不能攜帶param請求參數

“param=value”:要求請求映射所匹配的請求必須攜帶param請求參數且param=value

“param!=value”:要求請求映射所匹配的請求必須攜帶param請求參數但是param!=value

<a th:href="@{/test(username='admin',password=123456)">測試@RequestMapping的
params屬性-->/test</a><br>
@RequestMapping(value = {"/testRequestMapping", "/test"},method = {RequestMethod.GET, RequestMethod.POST},params = {"username","password!=123456"}
)
public String testRequestMapping(){return "success";
}

注:

若當前請求滿足@RequestMapping注解的value和method屬性,但是不滿足params屬性,此時

頁面回報錯400:Parameter conditions “username, password!=123456” not met for actual

request parameters: username={admin}, password={123456}

3.6、@RequestMapping注解的headers屬性(了解)

@RequestMapping注解的headers屬性通過請求的請求頭信息匹配請求映射

@RequestMapping注解的headers屬性是一個字符串類型的數組,可以通過四種表達式設置請求頭信

息和請求映射的匹配關系

“header”:要求請求映射所匹配的請求必須攜帶header請求頭信息

“!header”:要求請求映射所匹配的請求必須不能攜帶header請求頭信息

“header=value”:要求請求映射所匹配的請求必須攜帶header請求頭信息且header=value

“header!=value”:要求請求映射所匹配的請求必須攜帶header請求頭信息且header!=value

若當前請求滿足@RequestMapping注解的value和method屬性,但是不滿足headers屬性,此時頁面

顯示404錯誤,即資源未找到

3.7、SpringMVC支持ant風格的路徑

?:表示任意的單個字符

*:表示任意的0個或多個字符

**:表示任意層數的任意目錄

注意:在使用時,只能使用//xxx的方式

3.8、SpringMVC支持路徑中的占位符(重點)

原始方式:/deleteUser?id=1

rest方式:/user/delete/1

SpringMVC路徑中的占位符常用于RESTful風格中,當請求路徑中將某些數據通過路徑的方式傳輸到服

務器中,就可以在相應的@RequestMapping注解的value屬性中通過占位符{xxx}表示傳輸的數據,在

通過@PathVariable注解,將占位符所表示的數據賦值給控制器方法的形參

<a th:href="@{/testRest/1/admin}">測試路徑中的占位符-->/testRest</a><br>
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username")
String username){System.out.println("id:"+id+",username:"+username);return "success";
}
//最終輸出的內容為-->id:1,username:admin

4、SpringMVC獲取請求參數

4.1、通過ServletAPI獲取

將HttpServletRequest作為控制器方法的形參,此時HttpServletRequest類型的參數表示封裝了當前請求的請求報文的對象

@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){String username = request.getParameter("username");String password = request.getParameter("password");System.out.println("username:"+username+",password:"+password);return "success";
}

4.2、通過控制器方法的形參獲取請求參數

在控制器方法的形參位置,設置和請求參數同名的形參,當瀏覽器發送請求,匹配到請求映射時,在

DispatcherServlet中就會將請求參數賦值給相應的形參

<a th:href="@{/testParam(username='admin',password=123456)}">測試獲取請求參數--
>/testParam</a><br>
@RequestMapping("/testParam")
public String testParam(String username, String password){System.out.println("username:"+username+",password:"+password);return "success";
}

注:

若請求所傳輸的請求參數中有多個同名的請求參數,此時可以在控制器方法的形參中設置字符串

數組或者字符串類型的形參接收此請求參數

若使用字符串數組類型的形參,此參數的數組中包含了每一個數據

若使用字符串類型的形參,此參數的值為每個數據中間使用逗號拼接的結果

4.3、@RequestParam

@RequestParam是將請求參數和控制器方法的形參創建映射關系

@RequestParam注解一共有三個屬性:

value:指定為形參賦值的請求參數的參數名

required:設置是否必須傳輸此請求參數,默認值為true

若設置為true時,則當前請求必須傳輸value所指定的請求參數,若沒有傳輸該請求參數,且沒有設置

defaultValue屬性,則頁面報錯400:Required String parameter ‘xxx’ is not present;若設置為

false,則當前請求不是必須傳輸value所指定的請求參數,若沒有傳輸,則注解所標識的形參的值為

null

defaultValue:不管required屬性值為true或false,當value所指定的請求參數沒有傳輸或傳輸的值

為""時,則使用默認值為形參賦值

4.4、@RequestHeader

@RequestHeader是將請求頭信息和控制器方法的形參創建映射關系

@RequestHeader注解一共有三個屬性:value、required、defaultValue,用法同@RequestParam

4.5、@CookieValue

@CookieValue是將cookie數據和控制器方法的形參創建映射關系

@CookieValue注解一共有三個屬性:value、required、defaultValue,用法同@RequestParam

4.6、通過POJO獲取請求參數

可以在控制器方法的形參位置設置一個實體類類型的形參,此時若瀏覽器傳輸的請求參數的參數名和實體類中的屬性名一致,那么請求參數就會為此屬性賦值

<form th:action="@{/testpojo}" method="post">用戶名:<input type="text" name="username"><br>密碼:<input type="password" name="password"><br>性別:<input type="radio" name="sex" value=""><input type="radio"name="sex" value=""><br>年齡:<input type="text" name="age"><br>郵箱:<input type="text" name="email"><br><input type="submit">
</form>
@RequestMapping("/testpojo")
public String testPOJO(User user){System.out.println(user);return "success";
}
//最終結果-->User{id=null, username='張三', password='123', age=23, sex='男',
email='123@qq.com'}

4.7、解決獲取請求參數的亂碼問題

解決獲取請求參數的亂碼問題,可以使用SpringMVC提供的編碼過濾器CharacterEncodingFilter,但是必須在web.xml中進行注冊

<!--配置springMVC的編碼過濾器-->
<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><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

注:

SpringMVC中處理編碼的過濾器一定要配置到其他過濾器之前,否則無效

5、域對象共享數據

5.1、使用ServletAPI向request域對象共享數據

@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){request.setAttribute("testScope", "hello,servletAPI");return "success";
}

5.2、使用ModelAndView向request域對象共享數據

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){/*** ModelAndView有Model和View的功能* Model主要用于向請求域共享數據* View主要用于設置視圖,實現頁面跳轉*/ModelAndView mav = new ModelAndView();//向請求域共享數據mav.addObject("testScope", "hello,ModelAndView");//設置視圖,實現頁面跳轉mav.setViewName("success");return mav;
}

5.3、使用Model向request域對象共享數據

@RequestMapping("/testModel")
public String testModel(Model model){model.addAttribute("testScope", "hello,Model");return "success";
}

5.4、使用map向request域對象共享數據

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){map.put("testScope", "hello,Map");return "success";
}

5.5、使用ModelMap向request域對象共享數據

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){modelMap.addAttribute("testScope", "hello,ModelMap");return "success";
}

5.6、Model、ModelMap、Map的關系

Model、ModelMap、Map類型的參數其實本質上都是 BindingAwareModelMap 類型的

public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}

5.7、向session域共享數據

@RequestMapping("/testSession")
public String testSession(HttpSession session){session.setAttribute("testSessionScope", "hello,session");return "success";
}

5.8、向application域共享數據

@RequestMapping("/testApplication")
public String testApplication(HttpSession session){ServletContext application = session.getServletContext();application.setAttribute("testApplicationScope", "hello,application");return "success";
}

6、SpringMVC的視圖

SpringMVC中的視圖是View接口,視圖的作用渲染數據,將模型Model中的數據展示給用戶

SpringMVC視圖的種類很多,默認有轉發視圖和重定向視圖

當工程引入jstl的依賴,轉發視圖會自動轉換為JstlView

若使用的視圖技術為Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的視圖解析器,由此視圖解析器解析之后所得到的是ThymeleafView

6.1、ThymeleafView

當控制器方法中所設置的視圖名稱沒有任何前綴時,此時的視圖名稱會被SpringMVC配置文件中所配置的視圖解析器解析,視圖名稱拼接視圖前綴和視圖

后綴所得到的最終路徑,會通過轉發的方式實現跳轉

@RequestMapping("/testHello")
public String testHello(){return "hello";
}

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

6.2、轉發視圖

SpringMVC中默認的轉發視圖是InternalResourceView

SpringMVC中創建轉發視圖的情況:

當控制器方法中所設置的視圖名稱以"forward:"為前綴時,創建InternalResourceView視圖,此時的視圖名稱不會被SpringMVC配置文件中所配置的視圖解析器解析,而是會將前綴"forward:"去掉,剩余部分作為最終路徑通過轉發的方式實現跳轉

例如"forward:/",“forward:/employee”

@RequestMapping("/testForward")
public String testForward(){return "forward:/testHello";
}

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

6.3、重定向視圖

SpringMVC中默認的重定向視圖是RedirectView

當控制器方法中所設置的視圖名稱以"redirect:"為前綴時,創建RedirectView視圖,此時的視圖名稱不

會被SpringMVC配置文件中所配置的視圖解析器解析,而是會將前綴"redirect:"去掉,剩余部分作為最終路徑通過重定向的方式實現跳轉

例如"redirect:/",“redirect:/employee”

@RequestMapping("/testRedirect")
public String testRedirect(){return "redirect:/testHello";
}

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

注:

重定向視圖在解析時,會先將redirect:前綴去掉,然后會判斷剩余部分是否以/開頭,若是則會自

動拼接上下文路徑

6.4、視圖控制器view-controller

當控制器方法中,僅僅用來實現頁面跳轉,即只需要設置視圖名稱時,可以將處理器方法使用view

controller標簽進行表示

<!--path:設置處理的請求地址view-name:設置請求地址所對應的視圖名稱
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>

注:

當SpringMVC中設置任何一個view-controller時,其他控制器中的請求映射將全部失效,此時需

要在SpringMVC的核心配置文件中設置開啟mvc注解驅動的標簽:

<mvc:annotation-driven />

7、RESTful

7.1、RESTful簡介

REST:Representational State Transfer,表現層資源狀態轉移。

①資源

資源是一種看待服務器的方式,即,將服務器看作是由很多離散的資源組成。每個資源是服務器上一個可命名的抽象概念。因為資源是一個抽象的概念,所以它不僅僅能代表服務器文件系統中的一個文件、數據庫中的一張表等等具體的東西,可以將資源設計的要多抽象有多抽象,只要想象力允許而且客戶端應用開發者能夠理解。與面向對象設計類似,資源是以名詞為核心來組織的,首先關注的是名詞。一個資源可以由一個或多個URI來標識。URI既是資源的名稱,也是資源在Web上的地址。對某個資源感興趣的客戶端應用,可以通過資源的URI與其進行交互。

②資源的表述

資源的表述是一段對于資源在某個特定時刻的狀態的描述。可以在客戶端-服務器端之間轉移(交

換)。資源的表述可以有多種格式,例如HTML/XML/JSON/純文本/圖片/視頻/音頻等等。資源的表述格式可以通過協商機制來確定。請求-響應方向的表述通常使用不同的格式。

③狀態轉移

狀態轉移說的是:在客戶端和服務器端之間轉移(transfer)代表資源狀態的表述。通過轉移和操作資

源的表述,來間接實現操作資源的目的。

7.2、RESTful的實現

具體說,就是 HTTP 協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。

它們分別對應四種基本操作:GET 用來獲取資源,POST 用來新建資源,PUT 用來更新資源,DELETE

用來刪除資源。

REST 風格提倡 URL 地址使用統一的風格設計,從前到后各個單詞使用斜杠分開,不使用問號鍵值對方式攜帶請求參數,而是將要發送給服務器的數據作為 URL 地址的一部分,以保證整體風格的一致性。

操作傳統方式REST****風格
查詢操作getUserById?id=1user/1–>get請求方式
保存操作saveUseruser–>post請求方式
刪除操作deleteUser?id=1user/1–>delete請求方式
更新操作updateUseruser–>put請求方式

7.3、HiddenHttpMethodFilter

由于瀏覽器只支持發送get和post方式的請求,那么該如何發送put和delete請求呢?

SpringMVC 提供了 HiddenHttpMethodFilter 幫助我們 POST 請求轉換為 DELETE PUT 請求

HiddenHttpMethodFilter 處理put和delete請求的條件:

a>當前請求的請求方式必須為post

b>當前請求必須傳輸請求參數_method

滿足以上條件,HiddenHttpMethodFilter 過濾器就會將當前請求的請求方式轉換為請求參數

_method的值,因此請求參數_method的值才是最終的請求方式

在web.xml中注冊HiddenHttpMethodFilter

<filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass>
</filter>
<filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

注:

目前為止,SpringMVC中提供了兩個過濾器:CharacterEncodingFilter和

HiddenHttpMethodFilter

在web.xml中注冊時,必須先注冊CharacterEncodingFilter,再注冊HiddenHttpMethodFilter

原因:

  • 在 CharacterEncodingFilter 中通過 request.setCharacterEncoding(encoding) 方法設置字

符集的

  • request.setCharacterEncoding(encoding) 方法要求前面不能有任何獲取請求參數的操作

  • 而 HiddenHttpMethodFilter 恰恰有一個獲取請求方式的操作:

  • String paramValue = request.getParameter(this.methodParam);
    

8、RESTful案例

8.1、準備工作

和傳統 CRUD 一樣,實現對員工信息的增刪改查。

  • 搭建環境
  • 準備實體類
package com.atguigu.mvc.bean;
public class Employee {private Integer id;private String lastName;private String email;//1 male, 0 femaleprivate Integer gender;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender = gender;}public Employee(Integer id, String lastName, String email, Integergender) {super();this.id = id;this.lastName = lastName;this.email = email;this.gender = gender;}public Employee() {}
}
  • 準備dao模擬數據
package com.atguigu.mvc.dao;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.atguigu.mvc.bean.Employee;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeDao {private static Map<Integer, Employee> employees = null;static{employees = new HashMap<Integer, Employee>();employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));}private static Integer initId = 1006;public void save(Employee employee){if(employee.getId() == null){employee.setId(initId++);}employees.put(employee.getId(), employee);}public Collection<Employee> getAll(){return employees.values();}public Employee get(Integer id){return employees.get(id);}public void delete(Integer id){employees.remove(id);}
}

8.2、功能清單

功能URL 地址請求方式
訪問首頁√/GET
查詢全部數據√/employeeGET
刪除√/employee/2DELETE
跳轉到添加數據頁面√/toAddGET
執行保存√/employeePOST
跳轉到更新數據頁面√/employee/2GET
執行更新√/employeePUT

8.3、具體功能:訪問首頁

①配置view-controller

<mvc:view-controller path="/" view-name="index"/>

②創建頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8" ><title>Title</title></head><body><h1>首頁</h1><a th:href="@{/employee}">訪問員工信息</a></body>
</html>

8.4、具體功能:查詢所有員工數據

①控制器方法

@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getEmployeeList(Model model){Collection<Employee> employeeList = employeeDao.getAll();model.addAttribute("employeeList", employeeList);return "employee_list";
}

②創建employee_list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Employee Info</title><script type="text/javascript" th:src="@{/static/js/vue.js}"></script></head><body><table border="1" cellpadding="0" cellspacing="0" style="text-align:center;" id="dataTable"><tr><th colspan="5">Employee Info</th></tr><tr><th>id</th><th>lastName</th><th>email</th><th>gender</th><th>options(<a th:href="@{/toAdd}">add</a>)</th></tr><tr th:each="employee : ${employeeList}"><td th:text="${employee.id}"></td><td th:text="${employee.lastName}"></td><td th:text="${employee.email}"></td><td th:text="${employee.gender}"></td><td><a class="deleteA" @click="deleteEmployee"th:href="@{'/employee/'+${employee.id}}">delete</a><a th:href="@{'/employee/'+${employee.id}}">update</a></td></tr></table></body>
</html>

8.5、具體功能:刪除

①創建處理delete請求方式的表單

<!-- 作用:通過超鏈接控制表單的提交,將post請求轉換為delete請求 -->
<form id="delete_form" method="post"><!-- HiddenHttpMethodFilter要求:必須傳輸_method請求參數,并且值為最終的請求方式 --><input type="hidden" name="_method" value="delete"/>
</form>

引入vue.js

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>

刪除超鏈接

<a class="deleteA" @click="deleteEmployee"th:href="@{'/employee/'+${employee.id}}">delete</a>

通過vue處理點擊事件

<script type="text/javascript">var vue = new Vue({el:"#dataTable",methods:{//event表示當前事件deleteEmployee:function (event) {//通過id獲取表單標簽var delete_form = document.getElementById("delete_form");//將觸發事件的超鏈接的href屬性為表單的action屬性賦值delete_form.action = event.target.href;//提交表單delete_form.submit();//阻止超鏈接的默認跳轉行為event.preventDefault();}}});
</script>

③控制器方法

@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){employeeDao.delete(id);return "redirect:/employee";
}

8.6、具體功能:跳轉到添加數據頁面

①配置view-controller

<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>

②創建employee_add.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Add Employee</title></head><body><form th:action="@{/employee}" method="post">lastName:<input type="text" name="lastName"><br>email:<input type="text" name="email"><br>gender:<input type="radio" name="gender" value="1">male<input type="radio" name="gender" value="0">female<br><input type="submit" value="add"><br></form></body>
</html>

8.7、具體功能:執行保存

①控制器方法

@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){employeeDao.save(employee);return "redirect:/employee";
}

8.8、具體功能:跳轉到更新數據頁面

①修改超鏈接

<a th:href="@{'/employee/'+${employee.id}}">update</a>

②控制器方法

@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){Employee employee = employeeDao.get(id);model.addAttribute("employee", employee);return "employee_update";
}

③創建employee_update.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Update Employee</title></head><body><form th:action="@{/employee}" method="post"><input type="hidden" name="_method" value="put"><input type="hidden" name="id" th:value="${employee.id}">lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br>email:<input type="text" name="email" th:value="${employee.email}"><br><!--th:field="${employee.gender}"可用于單選框或復選框的回顯若單選框的value和employee.gender的值一致,則添加checked="checked"屬性-->gender:<input type="radio" name="gender" value="1"th:field="${employee.gender}">male<input type="radio" name="gender" value="0"th:field="${employee.gender}">female<br><input type="submit" value="update"><br></form></body>
</html>

8.9、具體功能:執行更新

①控制器方法

@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){employeeDao.save(employee);return "redirect:/employee";
}

9、SpringMVC處理ajax請求

9.1、@RequestBody

@RequestBody可以獲取請求體信息,使用@RequestBody注解標識控制器方法的形參,當前請求的請求體就會為當前注解所標識的形參賦值

<!--此時必須使用post請求方式,因為get請求沒有請求體-->
<form th:action="@{/test/RequestBody}" method="post">用戶名:<input type="text" name="username"><br>密碼:<input type="password" name="password"><br><input type="submit">
</form>
@RequestMapping("/test/RequestBody")
public String testRequestBody(@RequestBody String requestBody){System.out.println("requestBody:"+requestBody);return "success";
}

輸出結果:

requestBody:username=admin&password=123456

9.2、@RequestBody獲取json格式的請求參數

在使用了axios發送ajax請求之后,瀏覽器發送到服務器的請求參數有兩種格式:

1、name=value&name=value…,此時的請求參數可以通過request.getParameter()獲取,對應

SpringMVC中,可以直接通過控制器方法的形參獲取此類請求參數

2、{key:value,key:value,…},此時無法通過request.getParameter()獲取,之前我們使用操作

json的相關jar包gson或jackson處理此類請求參數,可以將其轉換為指定的實體類對象或map集

合。在SpringMVC中,直接使用@RequestBody注解標識控制器方法的形參即可將此類請求參數

轉換為java對象

使用@RequestBody獲取json格式的請求參數的條件:

1、導入jackson的依賴

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

2、SpringMVC的配置文件中設置開啟mvc的注解驅動

<!--開啟mvc的注解驅動-->
<mvc:annotation-driven />

3、在控制器方法的形參位置,設置json格式的請求參數要轉換成的java類型(實體類或map)的參

數,并使用@RequestBody注解標識

<input type="button" value="測試@RequestBody獲取json格式的請求參數"@click="testRequestBody()"><br>
<script type="text/javascript" th:src="@{/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/js/axios.min.js}"></script>
<script type="text/javascript">var vue = new Vue({el:"#app",methods:{testRequestBody(){axios.post("/SpringMVC/test/RequestBody/json",{username:"admin",password:"123456"}).then(response=>{console.log(response.data);});}}});
</script>
//將json格式的數據轉換為map集合
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody Map<String, Object> map,HttpServletResponse response) throws IOException {System.out.println(map);//{username=admin, password=123456}response.getWriter().print("hello,axios");
}
//將json格式的數據轉換為實體類對象
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody User user, HttpServletResponseresponse) throws IOException {System.out.println(user);//User{id=null, username='admin', password='123456', age=null,gender='null'}response.getWriter().print("hello,axios");
}

9.3、@ResponseBody

@ResponseBody用于標識一個控制器方法,可以將該方法的返回值直接作為響應報文的響應體響應到瀏覽器

@RequestMapping("/testResponseBody")
public String testResponseBody(){//此時會跳轉到邏輯視圖success所對應的頁面return "success";
}
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){//此時響應瀏覽器數據successreturn "success";
}

9.4、@ResponseBody響應瀏覽器json數據

服務器處理ajax請求之后,大多數情況都需要向瀏覽器響應一個java對象,此時必須將java對象轉換為

json字符串才可以響應到瀏覽器,之前我們使用操作json數據的jar包gson或jackson將java對象轉換為

json字符串。在SpringMVC中,我們可以直接使用@ResponseBody注解實現此功能

@ResponseBody響應瀏覽器json數據的條件:

1、導入jackson的依賴

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

2、SpringMVC的配置文件中設置開啟mvc的注解驅動

<!--開啟mvc的注解驅動-->
<mvc:annotation-driven />

3、使用@ResponseBody注解標識控制器方法,在方法中,將需要轉換為json字符串并響應到瀏覽器

的java對象作為控制器方法的返回值,此時SpringMVC就可以將此對象直接轉換為json字符串并響應到瀏覽器

<input type="button" value="測試@ResponseBody響應瀏覽器json格式的數據"@click="testResponseBody()"><br>
<script type="text/javascript" th:src="@{/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/js/axios.min.js}"></script>
<script type="text/javascript">var vue = new Vue({el:"#app",methods:{testResponseBody(){axios.post("/SpringMVC/test/ResponseBody/json").then(response=>{console.log(response.data);});}}});
</script>
//響應瀏覽器list集合
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public List<User> testResponseBody(){User user1 = new User(1001,"admin1","123456",23,"男");User user2 = new User(1002,"admin2","123456",23,"男");User user3 = new User(1003,"admin3","123456",23,"男");List<User> list = Arrays.asList(user1, user2, user3);return list;
}
//響應瀏覽器map集合
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public Map<String, Object> testResponseBody(){User user1 = new User(1001,"admin1","123456",23,"男");User user2 = new User(1002,"admin2","123456",23,"男");User user3 = new User(1003,"admin3","123456",23,"男");Map<String, Object> map = new HashMap<>();map.put("1001", user1);map.put("1002", user2);map.put("1003", user3);return map;
}
//響應瀏覽器實體類對象
@RequestMapping("/test/ResponseBody/json")
@ResponseBody
public User testResponseBody(){return user;
}

9.5、@RestController注解

@RestController注解是springMVC提供的一個復合注解,標識在控制器的類上,就相當于為類添加了

@Controller注解,并且為其中的每個方法添加了@ResponseBody注解

10、文件上傳和下載

10.1、文件下載

ResponseEntity用于控制器方法的返回值類型,該控制器方法的返回值就是響應到瀏覽器的響應報文

使用ResponseEntity實現下載文件的功能

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throwsIOException {//獲取ServletContext對象ServletContext servletContext = session.getServletContext();//獲取服務器中文件的真實路徑String realPath = servletContext.getRealPath("/static/img/1.jpg");//創建輸入流InputStream is = new FileInputStream(realPath);//創建字節數組byte[] bytes = new byte[is.available()];//將流讀到字節數組中is.read(bytes);//創建HttpHeaders對象設置響應頭信息MultiValueMap<String, String> headers = new HttpHeaders();//設置要下載方式以及下載文件的名字headers.add("Content-Disposition", "attachment;filename=1.jpg");//設置響應狀態碼HttpStatus statusCode = HttpStatus.OK;//創建ResponseEntity對象ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,statusCode);//關閉輸入流is.close();return responseEntity;
}

10.2、文件上傳

文件上傳要求form表單的請求方式必須為post,并且添加屬性enctype=“multipart/form-data”

SpringMVC中將上傳的文件封裝到MultipartFile對象中,通過此對象可以獲取文件相關信息

上傳步驟:

①添加依賴:

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

②在SpringMVC的配置文件中添加配置:

<!--必須通過文件解析器的解析才能將文件轉換為MultipartFile對象-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>

③控制器方法:

@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {//獲取上傳的文件的文件名String fileName = photo.getOriginalFilename();//處理文件重名問題String hzName = fileName.substring(fileName.lastIndexOf("."));fileName = UUID.randomUUID().toString() + hzName;//獲取服務器中photo目錄的路徑ServletContext servletContext = session.getServletContext();String photoPath = servletContext.getRealPath("photo");File file = new File(photoPath);if(!file.exists()){file.mkdir();}String finalPath = photoPath + File.separator + fileName;//實現上傳功能photo.transferTo(new File(finalPath));return "success";
}

11、攔截器

11.1、攔截器的配置

SpringMVC中的攔截器用于攔截控制器方法的執行

SpringMVC中的攔截器需要實現HandlerInterceptor

SpringMVC的攔截器必須在SpringMVC的配置文件中進行配置:

<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上兩種配置方式都是對DispatcherServlet所處理的所有的請求進行攔截 -->
<mvc:interceptor><mvc:mapping path="/**"/><mvc:exclude-mapping path="/testRequestEntity"/><ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!--以上配置方式可以通過ref或bean標簽設置攔截器,通過mvc:mapping設置需要攔截的請求,通過mvc:exclude-mapping設置需要排除的請求,即不需要攔截的請求
-->

11.2、攔截器的三個抽象方法

SpringMVC中的攔截器有三個抽象方法:

preHandle:控制器方法執行之前執行preHandle(),其boolean類型的返回值表示是否攔截或放行,返回true為放行,即調用控制器方法;返回false表示攔截,即不調用控制器方法

postHandle:控制器方法執行之后執行postHandle()

afterCompletion:處理完視圖和模型數據,渲染視圖完畢之后執行afterCompletion()

11.3、多個攔截器的執行順序

①若每個攔截器的preHandle()都返回true

此時多個攔截器的執行順序和攔截器在SpringMVC的配置文件的配置順序有關:

preHandle()會按照配置的順序執行,而postHandle()和afterCompletion()會按照配置的反序執行

②若某個攔截器的preHandle()返回了false

preHandle()返回false和它之前的攔截器的preHandle()都會執行,postHandle()都不執行,返回false

的攔截器之前的攔截器的afterCompletion()會執行

12、異常處理器

12.1、基于配置的異常處理

SpringMVC提供了一個處理控制器方法執行過程中所出現的異常的接口:HandlerExceptionResolver

HandlerExceptionResolver接口的實現類有:DefaultHandlerExceptionResolver和

SimpleMappingExceptionResolver

SpringMVC提供了自定義的異常處理器SimpleMappingExceptionResolver,使用方式:

<beanclass="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><props><!--properties的鍵表示處理器方法執行過程中出現的異常properties的值表示若出現指定異常時,設置一個新的視圖名稱,跳轉到指定頁面--><prop key="java.lang.ArithmeticException">error</prop></props></property><!--exceptionAttribute屬性設置一個屬性名,將出現的異常信息在請求域中進行共享--><property name="exceptionAttribute" value="ex"></property>
</bean>

12.2、基于注解的異常處理

//@ControllerAdvice將當前類標識為異常處理的組件
@ControllerAdvice
public class ExceptionController {//@ExceptionHandler用于設置所標識方法處理的異常@ExceptionHandler(ArithmeticException.class)//ex表示當前請求處理中出現的異常對象public String handleArithmeticException(Exception ex, Model model){model.addAttribute("ex", ex);return "error";}
}

13、注解配置SpringMVC

使用配置類和注解代替web.xml和SpringMVC配置文件的功能

13.1、創建初始化類,代替web.xml

在Servlet3.0環境中,容器會在類路徑中查找實現javax.servlet.ServletContainerInitializer接口的類,

如果找到的話就用它來配置Servlet容器。 Spring提供了這個接口的實現,名為

SpringServletContainerInitializer,這個類反過來又會查找實現WebApplicationInitializer的類并將配

置的任務交給它們來完成。Spring3.2引入了一個便利的WebApplicationInitializer基礎實現,名為

AbstractAnnotationConfigDispatcherServletInitializer,當我們的類擴展了

AbstractAnnotationConfigDispatcherServletInitializer并將其部署到Servlet3.0容器的時候,容器會自動發現它,并用它來配置Servlet上下文。

public class WebInit extendsAbstractAnnotationConfigDispatcherServletInitializer {/*** 指定spring的配置類* @return*/@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*** 指定SpringMVC的配置類* @return*/@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}/*** 指定DispatcherServlet的映射規則,即url-pattern* @return*/@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}/*** 添加過濾器* @return*/@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();encodingFilter.setEncoding("UTF-8");encodingFilter.setForceRequestEncoding(true);HiddenHttpMethodFilter hiddenHttpMethodFilter = newHiddenHttpMethodFilter();return new Filter[]{encodingFilter, hiddenHttpMethodFilter};}
}

13.2、創建SpringConfig配置類,代替spring的配置文件

@Configuration
public class SpringConfig {//ssm整合之后,spring的配置信息寫在此類中
}

13.3、創建WebConfig配置類,代替SpringMVC的配置文件

@Configuration
//掃描組件
@ComponentScan("com.atguigu.mvc.controller")
//開啟MVC注解驅動
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {//使用默認的servlet處理靜態資源@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}//配置文件上傳解析器@Beanpublic CommonsMultipartResolver multipartResolver(){return new CommonsMultipartResolver();}//配置攔截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {FirstInterceptor firstInterceptor = new FirstInterceptor();registry.addInterceptor(firstInterceptor).addPathPatterns("/**");}//配置視圖控制/*@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");}*///配置異常映射/*@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties prop = new Properties();prop.setProperty("java.lang.ArithmeticException", "error");//設置異常映射exceptionResolver.setExceptionMappings(prop);//設置共享異常信息的鍵exceptionResolver.setExceptionAttribute("ex");resolvers.add(exceptionResolver);}*///配置生成模板解析器@Beanpublic ITemplateResolver templateResolver() {WebApplicationContext webApplicationContext =ContextLoader.getCurrentWebApplicationContext();// ServletContextTemplateResolver需要一個ServletContext作為構造參數,可通過WebApplicationContext 的方法獲得ServletContextTemplateResolver templateResolver = newServletContextTemplateResolver(webApplicationContext.getServletContext());templateResolver.setPrefix("/WEB-INF/templates/");templateResolver.setSuffix(".html");templateResolver.setCharacterEncoding("UTF-8");templateResolver.setTemplateMode(TemplateMode.HTML);return templateResolver;}//生成模板引擎并為模板引擎注入模板解析器@Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {SpringTemplateEngine templateEngine = new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}//生成視圖解析器并未解析器注入模板引擎@Beanpublic ViewResolver viewResolver(SpringTemplateEngine templateEngine) {ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();viewResolver.setCharacterEncoding("UTF-8");viewResolver.setTemplateEngine(templateEngine);return viewResolver;}
}

13.4、測試功能

@RequestMapping("/")
public String index(){return "index";
}

14、SpringMVC執行流程

14.1、SpringMVC常用組件

  • DispatcherServlet:前端控制器,不需要工程師開發,由框架提供

作用:統一處理請求和響應,整個流程控制的中心,由它調用其它組件處理用戶的請求

  • HandlerMapping:處理器映射器,不需要工程師開發,由框架提供

作用:根據請求的url、method等信息查找Handler,即控制器方法

  • Handler:處理器,需要工程師開發

作用:在DispatcherServlet的控制下Handler對具體的用戶請求進行處理

  • HandlerAdapter:處理器適配器,不需要工程師開發,由框架提供

作用:通過HandlerAdapter對處理器(控制器方法)進行執行

  • ViewResolver:視圖解析器,不需要工程師開發,由框架提供

作用:進行視圖解析,得到相應的視圖,例如:ThymeleafView、InternalResourceView、

RedirectView

  • View:視圖

作用:將模型數據通過頁面展示給用戶

14.2、DispatcherServlet初始化過程

DispatcherServlet 本質上是一個 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏觀上是 Servlet生命周期來進行調度。

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

①初始化WebApplicationContext

所在類:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent -> set// the root application context (if any; may be null) as the parentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// No context instance was injected at construction time -> see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local one// 創建WebApplicationContextwac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {// 刷新WebApplicationContextonRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.// 將IOC容器在應用域共享String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}

②創建WebApplicationContext

所在類:org.springframework.web.servlet.FrameworkServlet

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {Class<?> contextClass = getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)){throw new ApplicationContextException("Fatal initialization error in servlet with name '" +getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}// 通過反射創建 IOC 容器對象ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());// 設置父容器wac.setParent(parent);String configLocation = getContextConfigLocation();if (configLocation != null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac;
}

③DispatcherServlet初始化策略

FrameworkServlet創建WebApplicationContext后,刷新容器,調用onRefresh(wac),此方法在

DispatcherServlet中進行了重寫,調用了initStrategies(context)方法,初始化策略,即初始化

DispatcherServlet的各個組件

所在類:org.springframework.web.servlet.DispatcherServlet

protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
}

14.3、DispatcherServlet調用組件處理請求

①processRequest()

FrameworkServlet重寫HttpServlet中的service()和doXxx(),這些方法中調用了

processRequest(request, response)

所在類:org.springframework.web.servlet.FrameworkServlet

protected final void processRequest(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException
{long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request,response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {// 執行服務,doService()是一個抽象方法,在DispatcherServlet中進行了重寫doService(request, response);}catch (ServletException | IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}logResult(request, response, failureCause, asyncManager);publishRequestHandledEvent(request, response, startTime, failureCause);}
}

②doService()

所在類:org.springframework.web.servlet.DispatcherServlet

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {logRequest(request);// Keep a snapshot of the request attributes in case of an include,// to be able to restore the original attributes after the include.Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap<>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {attributesSnapshot.put(attrName,request.getAttribute(attrName));}}}// Make framework objects available to handlers and view objects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request,response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}RequestPath requestPath = null;if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {requestPath = ServletRequestPathUtils.parseAndCache(request);}try {// 處理請求和響應doDispatch(request, response);}finally {if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}if (requestPath != null) {ServletRequestPathUtils.clearParsedRequestPath(request);}}
}

③doDispatch()

所在類:org.springframework.web.servlet.DispatcherServlet

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request./*mappedHandler:調用鏈包含handler、interceptorList、interceptorIndexhandler:瀏覽器發送的請求所匹配的控制器方法interceptorList:處理控制器方法的所有攔截器集合interceptorIndex:攔截器索引,控制攔截器afterCompletion()的執行*/mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.// 通過控制器方法創建相應的處理器適配器,調用所對應的控制器方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request,mappedHandler.getHandler());if (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {return;}}// 調用攔截器的preHandle()if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 由處理器適配器調用具體的控制器方法,最終獲得ModelAndView對象mv = ha.handle(processedRequest, response,mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 調用攔截器的postHandle()mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and otherscenarios.dispatchException = new NestedServletException("Handler dispatchfailed", err);}// 后續處理:處理模型數據和渲染視圖 processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processingfailed",err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
}                                                          

④processDispatchResult()

private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,@Nullable HandlerExecutionChainmappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered",exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler(): null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {// 處理模型數據和渲染視圖render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// Exception (if any) is already handled..// 調用攔截器的afterCompletion()mappedHandler.triggerAfterCompletion(request, response, null);}
}                                   

14.4、SpringMVC的執行流程

\1) 用戶向服務器發送請求,請求被SpringMVC 前端控制器 DispatcherServlet捕獲。

\2) DispatcherServlet對請求URL進行解析,得到請求資源標識符(URI),判斷請求URI對應的映射:

a) 不存在

i. 再判斷是否配置了mvc:default-servlet-handler

ii. 如果沒配置,則控制臺報映射查找不到,客戶端展示404錯誤

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

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

iii. 如果有配置,則訪問目標資源(一般為靜態資源,如:JS,CSS,HTML),找不到客戶端也會展示404

錯誤

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

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

b) 存在則執行下面的流程

\3) 根據該URI,調用HandlerMapping獲得該Handler配置的所有相關的對象(包括Handler對象以及

Handler對象對應的攔截器),最后以HandlerExecutionChain執行鏈對象的形式返回。

\4) DispatcherServlet 根據獲得的Handler,選擇一個合適的HandlerAdapter。

\5) 如果成功獲得HandlerAdapter,此時將開始執行攔截器的preHandler(…)方法【正向】

\6) 提取Request中的模型數據,填充Handler入參,開始執行Handler(Controller)方法,處理請求。

在填充Handler的入參過程中,根據你的配置,Spring將幫你做一些額外的工作:

a) HttpMessageConveter: 將請求消息(如Json、xml等數據)轉換成一個對象,將對象轉換為指定

的響應信息

b) 數據轉換:對請求消息進行數據轉換。如String轉換成Integer、Double等

c) 數據格式化:對請求消息進行數據格式化。 如將字符串轉換成格式化數字或格式化日期等

d) 數據驗證: 驗證數據的有效性(長度、格式等),驗證結果存儲到BindingResult或Error中

\7) Handler執行完成后,向DispatcherServlet 返回一個ModelAndView對象。

\8) 此時將開始執行攔截器的postHandle(…)方法【逆向】。

\9) 根據返回的ModelAndView(此時會判斷是否存在異常:如果存在異常,則執行

HandlerExceptionResolver進行異常處理)選擇一個適合的ViewResolver進行視圖解析,根據Model

和View,來渲染視圖。

\10) 渲染視圖完畢執行攔截器的afterCompletion(…)方法【逆向】。

\11) 將渲染結果返回給客戶端。

四、SSM整合

4.1、ContextLoaderListener

Spring提供了監聽器ContextLoaderListener,實現ServletContextListener接口,可監聽

ServletContext的狀態,在web服務器的啟動,讀取Spring的配置文件,創建Spring的IOC容器。web

應用中必須在web.xml中配置

<listener><!--配置Spring的監聽器,在服務器啟動時加載Spring的配置文件Spring配置文件默認位置和名稱:/WEB-INF/applicationContext.xml可通過上下文參數自定義Spring配置文件的位置和名稱--><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--自定義Spring配置文件的位置和名稱-->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring.xml</param-value>
</context-param>

4.2、準備工作

①創建Maven Module

②導入依賴

<packaging>war</packaging>
<properties><spring.version>5.3.1</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-beans</artifactId><version>${spring.version}</version></dependency><!--springmvc--><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>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><!-- Mybatis核心 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><!--mybatis和spring的整合包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency><!-- 連接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.9</version></dependency><!-- junit測試 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- MySQL驅動 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><!-- log4j日志 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->   <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.0</version></dependency><!-- 日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!-- ServletAPI --><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.12.1</version></dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency><!-- Spring5和Thymeleaf整合包 --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.12.RELEASE</version></dependency>
</dependencies> 

③創建表

CREATE TABLE `t_emp` (`emp_id` int(11) NOT NULL AUTO_INCREMENT,`emp_name` varchar(20) DEFAULT NULL,`age` int(11) DEFAULT NULL,`sex` char(1) DEFAULT NULL,`email` varchar(50) DEFAULT NULL,PRIMARY KEY (`emp_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

4.3、配置web.xml

<!-- 配置Spring的編碼過濾器 -->
<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><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置處理請求方式PUT和DELETE的過濾器 -->
<filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass>
</filter>
<filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC的前端控制器 -->
<servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass><!-- 設置SpringMVC的配置文件的位置和名稱 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:SpringMVC.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 設置Spring的配置文件的位置和名稱 -->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:Spring.xml</param-value>
</context-param>
<!-- 配置Spring的監聽器 -->
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

4.4、創建SpringMVC的配置文件并配置

<!--掃描組件-->
<context:component-scan base-package="com.atguigu.ssm.controller">
</context:component-scan>
<!--配置視圖解析器-->
<bean id="viewResolver"class="org.thymeleaf.spring5.view.ThymeleafViewResolver"><property name="order" value="1"/><property name="characterEncoding" value="UTF-8"/><property name="templateEngine"><bean class="org.thymeleaf.spring5.SpringTemplateEngine"><property name="templateResolver"><beanclass="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"><!-- 視圖前綴 --><property name="prefix" value="/WEB-INF/templates/"/><!-- 視圖后綴 --><property name="suffix" value=".html"/><property name="templateMode" value="HTML5"/><property name="characterEncoding" value="UTF-8" /></bean></property></bean></property>
</bean>
<!-- 配置訪問首頁的視圖控制 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!-- 配置默認的servlet處理靜態資源 -->
<mvc:default-servlet-handler />
<!-- 開啟MVC的注解驅動 -->
<mvc:annotation-driven />

4.5、搭建MyBatis環境

①創建屬性文件jdbc.properties

jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

②創建MyBatis的核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!--將下劃線映射為駝峰--><setting name="mapUnderscoreToCamelCase" value="true"/></settings><plugins><!--配置分頁插件--><plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin></plugins>
</configuration>

③創建Mapper接口和映射文件

public interface EmployeeMapper {
List<Employee> getEmployeeList();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ssm.mapper.EmployeeMapper"><select id="getEmployeeList" resultType="Employee">select * from t_emp</select>
</mapper>

④創建日志文件log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"><appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"><param name="Encoding" value="UTF-8" /><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /></layout></appender><logger name="java.sql"><level value="debug" /></logger><logger name="org.apache.ibatis"><level value="info" /></logger><root><level value="debug" /><appender-ref ref="STDOUT" /></root>
</log4j:configuration>

4.6、創建Spring的配置文件并配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--掃描組件--><context:component-scan base-package="com.atguigu.ssm"><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 引入jdbc.properties --><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!-- 配置Druid數據源 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置用于創建SqlSessionFactory的工廠bean --><bean class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 設置MyBatis配置文件的路徑(可以不設置) --><property name="configLocation" value="classpath:mybatis-config.xml"></property><!-- 設置數據源 --><property name="dataSource" ref="dataSource"></property><!-- 設置類型別名所對應的包 --><property name="typeAliasesPackage" value="com.atguigu.ssm.pojo"></property><!--設置映射文件的路徑若映射文件所在路徑和mapper接口所在路徑一致,則不需要設置--><!--<property name="mapperLocations" value="classpath:mapper/*.xml"></property>--></bean><!--配置mapper接口的掃描配置由mybatis-spring提供,可以將指定包下所有的mapper接口創建動態代理并將這些動態代理作為IOC容器的bean管理--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.atguigu.ssm.mapper"></property></bean>
</beans>

4.7、測試功能

①創建組件

實體類Employee

public class Employee {private Integer empId;private String empName;private Integer age;private String sex;private String email;public Employee() {}public Employee(Integer empId, String empName, Integer age, String sex,String email) {this.empId = empId;this.empName = empName;this.age = age;this.sex = sex;this.email = email;}public Integer getEmpId() {return empId;}public void setEmpId(Integer empId) {this.empId = empId;}public String getEmpName() {return empName;}public void setEmpName(String empName) {this.empName = empName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}

創建控制層組件EmployeeController

@Controller
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;@RequestMapping(value = "/employee/page/{pageNum}", method = RequestMethod.GET)public String getEmployeeList(Model model, @PathVariable("pageNum") Integer pageNum){PageInfo<Employee> page = employeeService.getEmployeeList(pageNum);model.addAttribute("page", page);return "employee_list";}
}

創建接口EmployeeService

public interface EmployeeService {PageInfo<Employee> getEmployeeList(Integer pageNum);
}

創建實現類EmployeeServiceImpl

@Service
public class EmployeeServiceImpl implements EmployeeService {@Autowiredprivate EmployeeMapper employeeMapper;@Overridepublic PageInfo<Employee> getEmployeeList(Integer pageNum) {PageHelper.startPage(pageNum, 4);List<Employee> list = employeeMapper.getEmployeeList();PageInfo<Employee> page = new PageInfo<>(list, 5);return page;}
}

②創建頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Employee Info</title><link rel="stylesheet" th:href="@{/static/css/index_work.css}"></head><body><table><tr><th colspan="6">Employee Info</th></tr><tr><th>emp_id</th><th>emp_name</th><th>age</th><th>sex</th><th>email</th><th>options</th></tr><tr th:each="employee : ${page.list}"><td th:text="${employee.empId}"></td><td th:text="${employee.empName}"></td><td th:text="${employee.age}"></td><td th:text="${employee.sex}"></td><td th:text="${employee.email}"></td><td><a href="">delete</a><a href="">update</a></td></tr><tr><td colspan="6"><span th:if="${page.hasPreviousPage}"><a th:href="@{/employee/page/1}">首頁</a><a th:href="@{'/employee/page/'+${page.prePage}}">上一頁</a></span><span th:each="num : ${page.navigatepageNums}"><a th:if="${page.pageNum==num}"th:href="@{'/employee/page/'+${num}}" th:text="'['+${num}+']'" style="color:red;"></a><a th:if="${page.pageNum!=num}"th:href="@{'/employee/page/'+${num}}" th:text="${num} "></a></span><span th:if="${page.hasNextPage}"><a th:href="@{'/employee/page/'+${page.nextPage}}">下一頁</a><a th:href="@{'/employee/page/'+${page.pages}}">末頁</a></span></td></tr></table></body>
</html>

③訪問測試分頁功能

t";
}
}


創建接口EmployeeService```java
public interface EmployeeService {PageInfo<Employee> getEmployeeList(Integer pageNum);
}

創建實現類EmployeeServiceImpl

@Service
public class EmployeeServiceImpl implements EmployeeService {@Autowiredprivate EmployeeMapper employeeMapper;@Overridepublic PageInfo<Employee> getEmployeeList(Integer pageNum) {PageHelper.startPage(pageNum, 4);List<Employee> list = employeeMapper.getEmployeeList();PageInfo<Employee> page = new PageInfo<>(list, 5);return page;}
}

②創建頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Employee Info</title><link rel="stylesheet" th:href="@{/static/css/index_work.css}"></head><body><table><tr><th colspan="6">Employee Info</th></tr><tr><th>emp_id</th><th>emp_name</th><th>age</th><th>sex</th><th>email</th><th>options</th></tr><tr th:each="employee : ${page.list}"><td th:text="${employee.empId}"></td><td th:text="${employee.empName}"></td><td th:text="${employee.age}"></td><td th:text="${employee.sex}"></td><td th:text="${employee.email}"></td><td><a href="">delete</a><a href="">update</a></td></tr><tr><td colspan="6"><span th:if="${page.hasPreviousPage}"><a th:href="@{/employee/page/1}">首頁</a><a th:href="@{'/employee/page/'+${page.prePage}}">上一頁</a></span><span th:each="num : ${page.navigatepageNums}"><a th:if="${page.pageNum==num}"th:href="@{'/employee/page/'+${num}}" th:text="'['+${num}+']'" style="color:red;"></a><a th:if="${page.pageNum!=num}"th:href="@{'/employee/page/'+${num}}" th:text="${num} "></a></span><span th:if="${page.hasNextPage}"><a th:href="@{'/employee/page/'+${page.nextPage}}">下一頁</a><a th:href="@{'/employee/page/'+${page.pages}}">末頁</a></span></td></tr></table></body>
</html>

③訪問測試分頁功能

localhost:8080/employee/page/1

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

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

相關文章

c語言實戰-極簡掃雷

C語言/c寫的C語言實戰項目掃雷 結構比較清晰&#xff0c;僅供參考&#xff1a; 核心是掃雷的遞歸算法實現 上代碼: #include <stdio.h> #include <stdlib.h> #include <time.h>#define SIZE 10 #define MINES 15char board[SIZE][SIZE]; // 游戲棋盤// 初…

Oracle的主要特點是什么?應用場景有哪些?

主要特點&#xff1a; 高可靠性&#xff1a;Oracle數據庫具有高度的可靠性&#xff0c;能夠確保數據的安全和穩定性。 高性能&#xff1a;提供高性能的數據處理和查詢能力&#xff0c;可以處理大規模的數據量。 良好的擴展性&#xff1a;支持水平和垂直的擴展&#xff0c;可以輕…

CloudWatch Logs Insights 詳解

CloudWatch Logs Insights 是 AWS 提供的強大日志分析工具,允許您快速、交互式地搜索和分析日志數據。本文將詳細介紹使用 CloudWatch Logs Insights 所需的權限、常用查詢方法,以及一些實用的查詢示例。 1. 所需權限 要使用 CloudWatch Logs Insights,用戶需要具備以下 I…

代碼隨想錄-Day55

42. 接雨水 給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖&#xff0c;計算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 輸入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 輸出&#xff1a;6 解釋&#xff1a;上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高…

CentOS7二進制安裝和YUM安裝mongodb,服務器無法安裝5.0以上的 mongodb 數據庫報錯 Illegal instruction

文章目錄 MongoDB 安裝二進制安裝YUM 安裝 Tips:1、MongoDB安裝問題2、MongoDB登錄3、MongoDB排序時內存大小限制和創建索引4、創建用戶5、Java yaml使用密碼連接mongodb6、MongoDB增刪改查 MongoDB 安裝 二進制安裝 [rootmysql5-7 mongodb-6.0.4]# cat start.sh #!/bin/bash…

js使用proxy代理監聽控制事件

本文為proxy代理的實例應用&#xff0c;有關代理的內容可以參考&#xff1a; js語法---理解反射Reflect對象和代理Proxy對象 監聽事件 要監聽dom元素的事件&#xff0c;我們會采用回調觸發的方式來執行操作&#xff0c; 而觸發事件的過程很明顯是一個異步操作&#xff0c;異…

Docker 使用基礎(1)—鏡像倉庫

&#x1f3ac;慕斯主頁&#xff1a;修仙—別有洞天 ??今日夜電波&#xff1a;秒針を噛む—ずっと真夜中でいいのに。 0:34━━━━━━?&#x1f49f;──────── 4:20 &#x1f504; ?? ? …

Pinia在vue項目中的使用

Pinia是Vue 3官方推薦的狀態管理模式&#xff0c;由尤雨溪創建并集成到了 Vue.js 中&#xff0c;它是一個輕量級、純粹基于函數的思想實現的應用狀態管理庫。Pinia的設計理念類似于Redux&#xff0c;但它更簡單易用&#xff0c;更適合于小型到中型的單文件組件應用。 在Vue 3項…

android13 固定U盤鏈接 SD卡鏈接 TF卡鏈接 硬盤鏈接

1.前言 有些客戶使用的應用并不帶有自動監聽U盤 sd卡廣播的代碼,使用的代碼是固定的地址,這樣的話,就需要我們將系統的掛載目錄固定了。 原始路徑 /storage/3123-19FA 增加鏈接 /storage/upan_000 -> /storage/3123-19FA 2. 首先如果是應用本身監聽的話,使用的是 /…

【Linux線程篇】探索Linux多線程:并行編程的入門指南

W...Y的主頁 &#x1f60a; 代碼倉庫分享&#x1f495; Linux線程概念 什么是線程 在一個程序里的一個執行路線就叫做線程&#xff08;thread&#xff09;。更準確的定義是&#xff1a;線程是“一個進程內部的控制序列”一切進程至少都有一個執行線程線程在進程內部運行&am…

【國產開源可視化引擎Meta2d.js】數據

數據 Meta2d.js是由數據驅動顯示的。圖紙和圖元支持任意數據。 內置屬性 基于“約定優于配置”原則&#xff0c;Meta2d.js引擎會有一些內置屬性名&#xff0c;例如id表示唯一標識、name表示圖元名稱、text表示文本、color表示顏色等。 內置屬性有固定含義&#xff0c;影響顯…

揭秘:離心風機風量背后的科學原理

在工業生產和建筑環境中&#xff0c;離心風機如同一位不倦的呼吸管家&#xff0c;默默地維持著空氣流動與品質。 你是否好奇過&#xff0c;究竟是什么因素在背后操縱著這位“呼吸管家”的風量表現呢&#xff1f;今天&#xff0c;就讓我們一探究竟。 舉個例子&#xff1a;你在吹…

『大模型筆記』GraphRAG:利用復雜信息進行發現的新方法!

GraphRAG:利用復雜信息進行發現的新方法! 文章目錄 一. GraphRAG:利用復雜信息進行發現的新方法!1. 將RAG應用于私人數據集2. 整個數據集的推理3. 創建LLM生成的知識圖譜4. 結果指標5. 下一步二. 參考文獻微軟官方推文:https://www.microsoft.com/en-us/research/blog/gra…

LeetCode題練習與總結:反轉字符串中的單詞--151

一、題目描述 給你一個字符串 s &#xff0c;請你反轉字符串中 單詞 的順序。 單詞 是由非空格字符組成的字符串。s 中使用至少一個空格將字符串中的 單詞 分隔開。 返回 單詞 順序顛倒且 單詞 之間用單個空格連接的結果字符串。 注意&#xff1a;輸入字符串 s中可能會存在…

速盾:好的cdn服務器

CDN&#xff08;Content Delivery Network&#xff09;是指內容分發網絡&#xff0c;是一種將網站的靜態內容&#xff08;如圖片、音頻、視頻&#xff09;緩存在分布式的服務器節點上&#xff0c;通過就近訪問用戶的請求&#xff0c;提供快速可靠的內容傳輸服務的技術。 好的C…

HTML5文本標簽、圖像標簽、超鏈接

一、文本樣式標簽 字體樣式標簽&#xff1a; 加粗&#xff1a;<strong>…</strong> 斜體&#xff1a; < em >…</ em> eg&#xff1a; <h3>徐志摩人物簡介</h3> <p> <strong>1910</strong>年入杭州學堂<br/> &l…

微信小程序 - 本地存儲 增加有效期

小程序的本地存儲API提供了wx.setStorageSync和wx.setStorage來存儲數據&#xff0c;注意的是&#xff0c;小程序的本地存儲并沒有明確的有效期設置&#xff0c;存儲的數據在不超過限制的情況下&#xff0c;會一直保留。 一、小程序本地存儲API 小程序的本地存儲API提供了設置…

WEB06JavaScriptAjax

基礎語法 引入方式 引入方式 內部腳本&#xff1a;將JS代碼定義在HTML頁面中 JavaScript代碼必須位于<script></script>標簽之間 在HTML文檔中&#xff0c;可以在任意地方&#xff0c;放置任意數量的<script> 一般會把腳本置于<body>元素的底部&a…

常見的氣體流量計有哪些?

1.氣體渦輪流量計 適用場合&#xff1a;流量變化小&#xff0c;脈動流頻率小&#xff0c;中低壓潔凈天然氣優點 1.精度高&#xff0c;重復性好 2.測量范圍廣&#xff0c;壓損小&#xff0c;安裝維修方便 3.具有較高的抗電磁干擾和抗震動能力缺點&#xff1a;分辨率低&#xff…

活動:不要卷模型,要卷應用

如何理解李彥宏說的“不要卷模型&#xff0c;要卷應用” 1. 現狀 現如今&#xff0c;是否具備獨立的 AI 技術似乎已經成為衡量一個互聯網企業是否成功的標志。各家都在竭盡全力卷模型、卷數據、卷訓練效果&#xff0c;市面上僅是語言類 AI 就多達十幾種&#xff0c;但從一名消…