Spring MVC整理

一、第一個Spring MVC程序

Spring框架結構圖

在這裏插入圖片描述

  • SpringMVC是Spring實現web模塊,用於簡化web開發

Spring MVC框架結構圖

Spring眼中的MVC在保留以前三者的基礎上增加了前端控制器。
在這裏插入圖片描述

HelloWorld程序

以在Eclipse中創建動態web工程爲例,需要配有tomcat開發環境。

創建動態web工程

在創建工程時,Dynamic web model version 推薦選擇2.5,若選擇3.0,則需要勾選上生成web.xml配置文件。

導包

核心包+aop(支持註解)+web+webmvc
在這裏插入圖片描述

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns="http://java.sun.com/xml/ns/javaee"  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  id="WebApp_ID" version="3.0">
  <display-name>01_springmvc_helloworld</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <!--SpirngMVC的思想是 有一個前端控制器能攔截所有請求, 並智能派發,  這個前端控制器是一個servlet -->
  <!-- The front controller of this Spring Web application,  responsible for handling all application requests -->
     <servlet>
           <servlet-name>springDispatcherServlet</servlet-name>
           <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
           <init-param>
           <!-- contextConfigLocation:指定springMVC配置文件位置  爲類路徑下的springmvc.xml -->
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
           </init-param>
           <!-- servlet啓動加載,servlet原本是第一次訪問創建對象
            load-on-startup 服務器啓動的時候創建對象 值越小優先級越高 越先創建對象 -->
           <load-on-startup>1</load-on-startup>
     </servlet>
     <!-- Map all requests to the DispatcherServlet for  handling -->
     <servlet-mapping>
           <servlet-name>springDispatcherServlet</servlet-name>
           <!--
                /*和/都是攔截所有請求; /不會攔截jsp
                /*的範圍更大; 還會攔截到*.jsp這些請求
            -->
           <url-pattern>/</url-pattern>
     </servlet-mapping>
  
</web-app>

配置springmvc.xml

在類路徑下創建springmvc.xml (spring bean configuration file),namespace中選擇 context掃描包組件
在這裏插入圖片描述

<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<context:component-scan base-package="com.syc.controller"></context:component-scan>
	
	<!-- 配置視圖解析器,能幫我們拼接頁面地址 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 視圖解析器自動拼串 -->
		<property name="prefix" value="/WEB-INF/pages/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
</beans>

前端頁面(jsp)

在這裏插入圖片描述
在前端頁面中,只是寫了一個get請求,然後我們需要編寫響應該請求的方法。

處理器Controller

在這裏插入圖片描述
啓動程序,在瀏覽器中點擊 hello, 自動跳轉到success頁面,並且在控制檯打印出“收到請求…正在處理中.”

HelloWold程序補充

1.不指定SpringMVC配置文件

在前面的HelloWorld程序中,我們用初始化參數指定了配置文件所在的位置
在這裏插入圖片描述
若我們沒有指定這個文件則會默認去找這個配置文件:/WEB-INF/servlet名-servlet.xml

2.url-pattern的攔截內容

/ 攔截所有請求,不攔截jsp頁面

/* 攔截所有請求,攔截jsp頁面

  • 爲什麼/會把index.html也攔截?
    在這裏插入圖片描述
    在這裏插入圖片描述

3.RequestMapping標註在類上

RequestMapping標註在類上,爲當前類所有的方法的請求地址指定一個基準路徑
在這裏插入圖片描述

二、RequestMapping中參數的設置

RequestMaping中的參數包括:method, params, headers, consumes, prodeces

1.method屬性規定請求方式

在這裏插入圖片描述
點擊鏈接是get方式,如果請求方式錯誤,會報405錯誤。

編寫jsp頁面,以post方式進行請求

<a href="test/handle02">handle02 Get方式不支持</a>
<br/>form表單方式支持post請求
<form action="test/handle02" method="post">
     <input type="submit">
</form>

2.params屬性規定請求參數

在這裏插入圖片描述

	@RequestMapping(value="/handle03",params= {"username=123","pwd","!age"})
	public String handle03() {
		return "success";
	}

3.headers屬性規定請求頭

/**
	 * 只能讓火狐進行訪問
	 * 谷歌
	 * User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
	 * Firefox:
	 * Mozilla/5.0 (Windo ws NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
	 * @return
	 */
	@RequestMapping(value="/handle04", headers="User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0")
	public String handle04() {
		System.out.println("handle04...");
		return "success";
	}

4.ant風格的url

編寫jsp頁面

<h3>Ant風格的url</h3>
<a href="antTest01">精確匹配</a>
<br/>
<a href="antTest02">單字符匹配</a>

編寫響應方法

package com.syc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class RequestMappingTest {
     
     /**
      *  Ant 風格資源地址支持 3 種匹配符:【瞭解】
           ?:匹配文件名中的一個字符
          *:匹配文件名中的任意個字符
          **:** 匹配多層路徑
      * @return
      */
     //精確匹配
     @RequestMapping("/antTest01")
     public String antTest01() {
           System.out.println("antTest01 output ...");
           return "success";
     }
     
     //1.匹配一個字符,0個或多個都不行
     @RequestMapping("/antTest0?")
     public String antTest02() {
           System.out.println("antTest02 output ...");
           return "success";
     }
     
     //2.匹配任意多個字符,0個或多個都行
     @RequestMapping("/antTest0*")
     public String antTest03() {
           System.out.println("antTest03 output ...");
           return "success";
     }
     
     //3.匹配一層路徑
     @RequestMapping("/a/*/antTest0*")
     public String antTest04() {
           System.out.println("antTest04 output ...");
           return "success";
     }
     
     //4.匹配多層路徑,0層或多層
     @RequestMapping("/a/**/antTest0*")
     public String antTest05() {
           System.out.println("antTest05 output ...");
           return "success";
     }
     
}

小結:ant風格的url,在能夠滿足精確匹配的情況下,優先考慮精確匹配

5.使用@PathVariable獲取路徑上的佔位符

//路徑上可以有佔位符,佔位符的語法就是可以在任意路徑的地方寫一個{變量名}
	@RequestMapping("/user/{pwdd}")
	public String pathVariableTest(@PathVariable("pwdd")String pwd) {
		System.out.println("路徑上的佔位符爲:" + pwd);
		return "success";
	}

如訪問:http://localhost:8080/01_springmvc_helloworld/user/syc

輸出: 路徑上的佔位符爲:syc

三、Rest風格的URL地址(get, post, put, delete)

核心思想是:配置filter,這個filter是HiddenHttpMethodFilter

  • 使用Rest風格的url地址,在對資源進行增刪改查操作,無需在地址中專門寫四個不同的請求方式:GET、POST、PUT、DELETE
  • 使用Rest風格地址,需要在web.xml中配置Filter,它可以把普通形式的請求轉化爲規定形式的請求,配置這個filter爲 HiddenHttpMethodFilter
  • 如何發其他形式的請求?1、創建一個post類型的表單 2、表單攜帶一個_method的參數 3、這個_method的值就是DELETE、PUT
  • Get查詢,POST添加,PUT更新,DELETE刪除

配置web.xml

主要是編寫HiddenHttpMethodFilter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>02_springmvc_rest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<filter>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

jsp前端頁面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 發起圖書的增刪改查請求,使用Rest風格的URL地址
	從頁面發起PUT、DELETE形式的請求
	1)SpringMVC中有一個Filter,它可以把普通形式的請求轉化爲規定形式的請求,配置這個filter爲 HiddenHttpMethodFilter
	2)如何發其他形式的請求?1、創建一個post類型的表單 2、表單攜帶一個_method的參數 3、這個_method的值就是DELETE、PUT
 -->

<a href="book/1">查詢圖書</a>
<form action="book" method="post">
	<input type="submit" value="添加圖書">
</form>
<form action="book/1" method="post">
	<input name="_method" value="delete">
	<input type="submit" value="刪除1號圖書">
</form>
<form action="book/1" method="post">
	<input name="_method" value="put">
	<input type="submit" value="更新1號圖書">
</form>
</body>
</html>

控制器類Controller

package com.syc.controller;

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

@Controller
public class BookController {
	
	@RequestMapping(value="/book/{bid}", method=RequestMethod.GET)
	public String getBook(@PathVariable("bid")Integer id) {
		System.out.println("查詢到了"+id+"號圖書");
		return "success";
	}
	@RequestMapping(value="/book/{bid}", method=RequestMethod.DELETE)
	public String deleteBook(@PathVariable("bid")Integer id) {
		System.out.println("刪除了"+id+"號圖書");
		return "success";
	}
	@RequestMapping(value="/book/{bid}", method=RequestMethod.PUT)
	public String updateBook(@PathVariable("bid")Integer id) {
		System.out.println("更新了"+id+"號圖書");
		return "success";
	}
	@RequestMapping(value="/book", method=RequestMethod.POST)
	public String addBook() {
		System.out.println("添加了新的圖書");
		return "success";
	}

}

在這裏插入圖片描述
輸出:
在這裏插入圖片描述

源碼分析

在HiddenHttpMethodFilter中有一個doFilterInternal方法:
在這裏插入圖片描述
其中的methodParam的值爲:
在這裏插入圖片描述
如果請求方式爲POST,並且paramValue有值,則將該值轉化爲大寫,並且放到HttpServletRequest中,放行時用wrapper放行,否則直接用request進行放行。

而其中new HttpMethodRequestWrapper(request, method); 的具體做法是:獲取到傳進來的method,然後重寫其中的getMethod()方法
在這裏插入圖片描述

解決瀏覽器報錯問題

在響應delete和put方法請求時,若使用tomcat8及更高的版本,瀏覽器頁面會報錯HTTP Status 405 - JSPs only permit GET POST or HEAD

解決方法:在要跳轉的success.jsp中寫入isErrorPage=“true”

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isErrorPage="true"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
成功
</body>
</html>

四、Request請求處理

請求處理的方式:

1、默認方式獲取請求參數

2、@RequestParam 獲取請求參數

3、@RequestHeader 獲取請求頭信息

4、@CookieValue 獲取cookie的JID值

1.默認方式獲取請求參數

直接給方法入參上寫一個和請求參數名相同的變量,這個變量就來接受請求參數的值。

請求時帶了參數:有值;沒帶參數:null

  • jsp頁面加上請求參數
<a href="handle01?username=syc">Hello</a><br>
  • 控制器中將參數寫在方法中獲取,要求參數名要一樣
	@RequestMapping("/handle01")
	public String hello(String username) {
		System.out.println("Hello ..." + username);
		return "success";
	}

說明:若請求的url爲:http://localhost:8080/03_springmvc_request/handle01?

即在handle01後未添加username參數,則打印的username爲null
在這裏插入圖片描述

2.@RequestParam等方式

  • @RequestParam 獲取請求參數值,參數默認是必須帶的
    • value:指定要獲取的參數的key
    • required:選擇參數是否必須
    • defaultValue:默認值,沒帶默認是null
  • @RequestHeader 獲取請求頭信息
    • value:指定要獲取的參數的key
    • required:選擇參數是否必須
    • defaultValue:默認值,沒帶默認是null
  • @CookieValue 獲取cookie的JID值(JSESSIONID值,會話關掉,第一次獲取的時候沒有JSESSIONID的值)

編寫JSP頁面代碼

<a href="handle02?username=syc">handle02</a><br/>

編寫控制器類

	@RequestMapping("/handle02")
	public String handle02(@RequestParam(value="user", required=false, defaultValue="我沒帶")String username, 
			@RequestHeader("User-Agent")String userAgent,
			@CookieValue("JSESSIONID")String jid) {
		System.out.println("變量的值:"+username);
		System.out.println("請求頭中的瀏覽器信息:"+userAgent);
		System.out.println("cookie中的jid的值"+jid);
		return "success";
	}

請求地址:http://localhost:8080/03_springmvc_request/handle02?username=syc
該請求對應的輸出:
在這裏插入圖片描述

3.傳入POJO,自動封裝

如果請求參數是一個POJO,SpringMVC會自動地爲這個POJO進行賦值。

以在jsp表單中添加一本書爲例,在後端中獲取圖書信息

編寫JSP頁面

<form action="book" method="post">
	書名:<input type="text" name="bookName"/><br/>
	作者:<input type="text" name="author"/><br/>
	價格:<input type="text" name="price"/><br/>
	庫存:<input type="text" name="stock"/><br/>
	銷量:<input type="text" name="sales"/><br/>
	<hr/>
    <!-- 級聯封裝 -->
	省:<input type="text" name="address.CityName"/><br/>
	街道:<input type="text" name="address.Street"/><br/>
	<input type="submit">
</form>

在這裏插入圖片描述
編寫圖書的POJO

package com.syc.book;

public class Book {
	/** 
	書名:<input type="text" name="bookName"/><br/>
	作者:<input type="text" name="author"/><br/>
	價格:<input type="text" name="price"/><br/>
	庫存:<input type="text" name="stock"/><br/>
	銷量:<input type="text" name="sales"/><br/>
	 */
	private String bookName;
	private String author;
	private double price;
	private Integer stock;
	private Integer sales;
	private Address address;
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public Integer getStock() {
		return stock;
	}
	public void setStock(Integer stock) {
		this.stock = stock;
	}
	public Integer getSales() {
		return sales;
	}
	public void setSales(Integer sales) {
		this.sales = sales;
	}
	
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", author=" + author + ", price=" + price + ", stock=" + stock
				+ ", sales=" + sales + ", address=" + address + "]";
	}
		
}
package com.syc.book;

public class Address {
	private String CityName;
	private String Street;
	public String getCityName() {
		return CityName;
	}
	public void setCityName(String cityName) {
		CityName = cityName;
	}
	public String getStreet() {
		return Street;
	}
	public void setStreet(String street) {
		Street = street;
	}
	@Override
	public String toString() {
		return "Address [CityName=" + CityName + ", Street=" + Street + "]";
	}
	
}

處理響應

/**
	 * 如果請求參數是一個POJO
	 * SpringMVC會自動地爲這個POJO進行賦值
	 * 1、將POJO中的每一個屬性,從request參數中嘗試獲取出來,並封裝即可,相當於POJO中有一個參數,便使用RequestParam獲取該參數
	 * 2、還可以級聯封裝:屬性的屬性
	 * @param book
	 * @return
	 */	
@RequestMapping("/book")
	public String addBook(Book book) {
		System.out.println("我要保存的圖書:"+book);
		return "success";
	}

響應請求
在這裏插入圖片描述

解決獲取中文時的亂碼問題

亂碼的分兩種情況:

1.請求亂碼

GET請求需要修改總的服務器的server.xml

​ 在8080端口的Connector中加入URIEncoding=“UTF-8”,即

<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

POST請求:在第一次獲取請求參數之前設置request.setCharacterEncoding(“UTF-8”);

​ 也可以自己寫一個filter,SpringMVC中有這個Filter,是 CharacterEncodingFilter

<!-- 字符編碼的filter最先配置 -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<!-- encoding:指定解決POST請求亂碼 -->
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
		<!-- 順手解決響應亂碼:response.setCharacterEncoding(this.encoding); -->
			<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>

CharacterEncodingFilter的源碼部分:
在這裏插入圖片描述
在這裏插入圖片描述
小結:使用SpingMVC前端控制器寫完就直接寫字符編碼過濾器(解決POST請求亂碼問題);對於GET請求的亂碼問題,應該是Tomcat一裝好,就修改server.xml的8080處添加URIEncoding=“UTF-8”;並且設置字符編碼的filter應該在其他filter之前,因爲要在第一次獲取請求參數之前設置字符編碼,若其他的filter已經獲取請求參數,在此filter之後設置字符編碼就無效。

2.響應亂碼

響應亂碼很好解決:response.setContentType(“text/html;charset=utf-8”);

4.傳入原生API

這裏的原生API指HttpServletRequest、HttpSession等,直接寫在參數中

請求地址

http://localhost:8080/03_springmvc_request/handle03

處理響應

可以用的原生API主要有三個:HttpServletRequest、HttpServletResponse、HttpSession

/**
	 * SpringMVC可以直接在參數上寫原生API,可以寫的原生API有如下幾個
	 * HttpServletRequest
	 * HttpServletResponse
	 * HttpSession
	 * 
	 * java.security.Principal:https安全協議相關
	 * Locale:國際化有關的區域信息
	 * InputStream:
	 * 	   ServletInputStream inputStream = request.getInputStream();
	 * OutputStream:
	 * 		ServletOutputStream outputStream = response.getOutputStream();
	 * Reader
	 * 		BufferedReader reader = request.getReader();
	 * Writer
	 * 		PrintWriter writer = response.getWriter();
	 * @return
	 */
	@RequestMapping("/handle03")
	public String handle03(HttpSession session, HttpServletRequest request) {
		request.setAttribute("reqParam", "我是請求域中的");
		session.setAttribute("sessionParam", "我是session域中的");
		return "success";
	}

編寫跳轉成功的JSP頁面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
恭喜!
<h1>成功!</h1>
請求:${requestScope.reqParam}<br/>
session: ${sessionScope.sessionParam }<br/>
</body> 
</html>

跳轉成功
在這裏插入圖片描述

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