url-pattern
① /===> 除了.jsp的請求都會被匹配 (推薦使用)
② /*===> 所有的請求都會匹配
③ *.do、*.action===> 結尾以.do或者.action的請求會匹配
④ /request/*===> 要進行約定 將jsp放在/views/ 所有的servlet請求都用/request/
<!--web.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化參數
contextConfigLocation 配置springmvc的xml配置文件,指定路徑
也可以不配置: 會自動WEB-INF去找, springmvc-servlet.xml文件
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--啟動時加載servlet:
當web服務器 啟動時就會創建servlet(會自動調用servlet的構造函數及init()方法)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置DispatcherServlet映射
springmvc映射的路徑為:
/ 除了.jsp的請求都會被匹配
/* 所有的請求都會匹配
*.do *.action url結尾以.do或者.action的請求會匹配
/request/* 要進行約定 將jsp放在/views/ 所有的servlet請求都用/request/
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<!--配置編碼過濾器, 解決中文亂碼問題-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--encoding編碼格式-->
<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請求進行匹配-->
<!--<url-pattern></url-pattern>-->
<!--② 指定過濾哪個servlet-->
<servlet-name>springmvc</servlet-name>
</filter-mapping
》》》RequestParam:獲取請求的參數
package com.demo.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 在springmvc中只需要在處理方法中聲明對應的的參數就可以自動接收請求的參數并且還可以自動轉換類型
*
* 如果類型未匹配會報400
*
* 基本數據類型匹配規則:
* 請求的參數必須跟處理方法的參數名一致
* 如果處理方法的參數未傳入的情況會自動傳入null
*
* 請求的參數跟處理方法的參數不一致
* @RequestParam 管理請求參數
* value: 用來重命名參數
* required: 用來指定參數是否必須傳入值
* true:默認 必須傳入值,不傳值會報400
* false: 可以不用傳入值
* defaultValue: 當參數為null的時候會自動設置一個默認值
* 注意:基本數據類型是無法接收null;當設置了默認值可以省略required=false
* 處理請求參數亂碼:
* GET: 直接設置tomcat目錄下conf/server.xml 里面<Connector URIEncoding="UTF-8"
* POST: ① 在servlet, 在獲取參數之前 設置request.setCharacterEncoding("UTF-8"),每個請求都需要處理一次,非常麻煩
* ② 推薦使用過濾器處理亂碼問題。
* ③ 在Spring MVC, 解決亂碼問題:CharacterEncodingFilter, 需在web.xml中配置
*
*/
@Controller
public class ParamsController {
@RequestMapping("/params")
public String params(@RequestParam(value="username", required=true, defaultValue="heihei") String name) {
System.out.println("name=" + name);
// 響應
// redirect 重定向
// forward 轉發===> 默認
return "/index.jsp";
}
}
package com.demo.controllers.entity;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class User {
private Integer id;
private String name;
private String[] alias;
private List<String> hobbies;
private Map<String, String> relatives;
private Role role;
private List<User> friends;
}
package com.demo.controllers.entity;
import lombok.Data;
@Data
public class Role {
private Integer id;
private String name;
}
package com.demo.controllers.entity;
import lombok.Data;
@Data
public class UserDTO {
private User user;
private Role role;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>簡單參數</h2>
<%--${pageContext.request.contextPath}: 動態獲取項目路徑--%>
<form action="${pageContext.request.contextPath}/params01" method="post">
姓名:<input name="username" type="text" /> <br />
<input type="submit" value="提交">
</form>
<h2>復雜參數</h2>
<form action="${pageContext.request.contextPath}/params02" method="post">
id:<input name="user.id" type="text" /> <br />
姓名:<input name="user.name" type="text" /> <br />
別名: <input name="user.alias" type="checkbox" value="AA1" checked />AA1 <br />
<input name="user.alias" type="checkbox" value="AA2" checked />AA2
愛好: <input name="user.hobbies" type="checkbox" value="BB1" checked />BB1 <br />
<input name="user.hobbies" type="checkbox" value="BB2" checked />BB2
親屬: <input name="user.relatives['k1']" type="checkbox" value="CC1" checked />CC1 <br />
<input name="user.relatives['k2']" type="checkbox" value="CC2" checked />CC2
角色:<input name="user.role.name" type="text" /> <br />
朋友:<input name="user.friends[0].name" type="text" value="張三"/> <br />
<input name="user.friends[1].name" type="text" value="李四"/> <br />
<h2>新增角色</h2>
id:<input name="role.id" type="text" /> <br />
角色:<input name="role.name" type="text" /> <br />
<input type="submit" value="提交">
</form>
</body>
</html>
package com.demo.controllers;
import com.demo.controllers.entity.User;
import com.demo.controllers.entity.UserDTO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 在springmvc中只需要在處理方法中聲明對應的的參數就可以自動接收請求的參數并且還可以自動轉換類型
*
* 如果類型未匹配會報400
*
* 類型匹配規則:
* 請求的參數必須跟處理方法的參數名一致
* 如果處理方法的參數未傳入的情況會自動傳入null
*
* 請求的參數跟處理方法的參數不一致
* 基本數據類型參數:
* @RequestParam 管理請求參數
* value: 用來重命名參數
* required: 用來指定參數是否必須傳入值
* true:默認 必須傳入值,不傳值會報400
* false: 可以不用傳入值
* defaultValue: 當參數為null的時候會自動設置一個默認值
* 注意:基本數據類型是無法接收null;當設置了默認值可以省略required=false
*
* 復雜類型參數:
* 不用加上對象,直接傳入對象的屬性名
* 包裝類型:直接輸入屬性名===> name="id"
* 數組: 表單元素要同樣的name===> name="alias"
* List: 必須加上[index]===> name=list[0] 如果List<User> name="list[0].name"
* Map: 必須加上[key]===> name="map['key']"
* 實體類: 只能給某個屬性賦值===> name="object.xxx"
* 注意: 如果出現多個對象(User user, Role role)參數的情況 建議再次封裝一層javaBean (DTO data transfer object)
*/
@Controller
public class ParamsController {
@RequestMapping("/params01")
public String params01(@RequestParam(value="username", required=true, defaultValue="heihei") String name) {
System.out.println("name=" + name);
// 響應
// redirect 重定向
// forward 轉發===> 默認
return "/index.jsp";
}
@RequestMapping("/params02")
public String params02(UserDTO userDTO) {
System.out.println("userDTO=" + userDTO);
return "/index.jsp";
}
}
》》》RequestHeader:獲取請求頭信息
/**
* 獲取請求頭信息
* @param host
* @return
*/
@RequestMapping("/header")
public String header(@RequestHeader("host") String host) {
System.out.println("host=" + host);
return "/index.jsp";
}
》》》CookieValue:獲取cookie的值
/**
* 獲取cookie
* JSESSIONID: 客戶端發送請求時,為了保證客戶端和服務端數據一致性,服務端響應一個JSESSIONID給客戶端,實際JSESSIONID依賴cookie
* @param jsSessionId
* @return
*/
@RequestMapping("/cookie")
public String cookie(@CookieValue("JSESSIONID") String jsSessionId){
System.out.println("jsSessionId=" + jsSessionId);
return "/index.jsp";
}
》》》Spring MVC + Servlet API
/**
* 使用原生的servlet API, 但未解決耦合問題
* Spring MVC 可以和 Servlet API結合一起使用
* @param request
* @param response
* @return
*/
@RequestMapping("/servlet")
public String servlet(String username, HttpServletRequest request, HttpServletResponse response) {
System.out.println("username=" + username);
String name=request.getParameter("name");
System.out.println("name=" + name);
request.setAttribute("name", name);
return "/index.jsp";
}
處理URL映射,將請求映射到處理方法中
如果放在類上,所有的方法都會被映射,進行模塊化,防止方法中的請求重復映射
》》》RequestMapping| GetMapping/PostMapping
@RequestMapping(value="/mapping01", method=RequestMethod.POST)
public String mapping01() {
System.out.println("映射成功");
return "/index.jsp";
}
@PostMapping(value="/mapping02")
public String mapping02() {
System.out.println("請求方式等同于上方的請求方式");
return "/index.jsp";
}
》》》params
/**
* params: 設置請求必須攜帶某些參數
* 1. 必須要有某些參數 params={"username"}
* 2. 必須沒有某些參數 params={"!username"}
* 3. 必須等于某值 params={"username=liliu"}
* 4. 必須不等于某值 params={"username!=liliu"}
* @return
*/
@RequestMapping(value="/params", params={"username"})
public String params() {
System.out.println("請求參數!");
return "/index.jsp";
}
》》》headers
/**
* 請求頭必須包含指定的值
* @return
*/
@RequestMapping(value="/headers", headers={"Connection: keep-alive"})
public String headers() {
System.out.println("請求映射!");
return "/index.jsp";
}
》》》consumes===> 請求類型
/**
* consumes: 當前請求的內容類型必須為指定值
* 請求內容類型不匹配,報415
*
* @return
*/
@RequestMapping(value="/consumes", consumes={"application/x-www-form-urlencoded"})
public String consumes() {
System.out.println("請求映射!");
return "/index.jsp";
}
》》》produces===> 響應內容類型
/**
* 設置當前響應的內容類型
* @return
*/
@RequestMapping(value="/produces", produces={"application/json"})
public String produces() {
System.out.println("響應內容類型參數!");
return "/index.jsp";
}
/**
* 通配符的映射
* @return
*/
@RequestMapping(value="/**/ant")
public String mapping06() {
System.out.println("通配符 **");
return "/index.jsp";
}
/**
* @PathVariable 用在參數上面的
* 專門來獲取URL目錄級別的參數
* 單個參數接收必須要使用@PathVariable來聲明對應的參數占位符名字
* @param id
* @param username
* @return
*/
@RequestMapping("/user/{id}/{username}")
public String path01(@PathVariable("id") Integer id, @PathVariable("username") String username) {
System.out.println("id=" + id);
System.out.println("username=" + username);
return "/index.jsp";
}
/**
* javaBean可以省略@PathVariable, 但是要保證占位符的名字和javaBean的屬性名一致
* @param user
* @return
*/
@RequestMapping("/user02/{id}/{name}")
public String path02(User user) {
System.out.println("user=" + user);
return "/index.jsp";
}
表述性狀態傳遞(Representational State Tranfer, 簡稱REST)
RESTful:一種優雅的URL風格
》》》URL CRUD
》》》REST CRUD
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% request.setAttribute("basepath", request.getContextPath()); %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>簡單參數</h2>
<%--${pageContext.request.contextPath}: 動態獲取項目路徑--%>
<form action="${basepath}/params01" method="post" >
</form>
</body>
</html>
解決報405的錯誤
form表單提交PUT和DELETE出現問題:會將PUT和DELETE作為GET提交,因為HTML無法支持PUT和DELETE
解決方案:
① 添加HiddenHttpMethodFilter過濾器
<!--處理HTML表單不支持REST中PUT和DELETE-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
② 在表單中添加隱藏域
<form action="${basepath}/params01" method="post" >
<!--form的method:post, value: put或delete請求方式-->
<input type="hidden" name="_method" value="put" />
</form>
》》》基于服務API的方式使用restful
package com.demo.controllers;
import com.demo.controllers.entity.User;
import org.springframework.web.bind.annotation.*;
/**
* 基于服務API的方式使用RESTful
* 返回的數據基本是: JSON格式的
*/
@RestController
@RequestMapping("/restful")
public class RestfulController {
// 修改
@PutMapping("/user/{id}")
public String update(User user) {
System.out.println("修改用戶: " + user);
// 返回的數據是JSON格式
return "{'msg':'success'}";
}
// 刪除
@DeleteMapping("/user/{id}")
public String delete(@PathVariable("id") Integer id) {
System.out.println("刪除用戶: " + id);
// 返回的數據是JSON格式
return "{'msg':'success'}";
}
}
:500錯誤
1、500 Internal Server Error 內部服務錯誤:顧名思義500錯誤?般是服務器遇到意外情況,??法完成請求。
2、500出錯的可能性:
a、編程語?語法錯誤,web腳本錯誤
b、并發?時,因為系統資源限制,?不能打開過多的?件
3、?般解決思路:
a、查看nginx、php的錯誤?志?件,從?看出端倪
b、如果是too many open files,修改nginx的worker_rlimit_nofile參數,使?ulimit查看系統打開?件限制,修
改/etc/security/limits.conf,還是出現too many open files,那就要考慮做負載均衡,把流量分散到不同服務器上去了
c、如果是腳本的問題,則需要修復腳本錯誤,優化代碼
?:502、504錯誤
1、502 Bad Gateway錯誤、504 Bad Gateway timeout ?關超時
2、502、504出現的可能性
web服務器故障、程序進程不夠
3、?般解決思路
a、使?nginx代理,?后端服務器發?故障;或者php-cgi進程數不夠?;php執?時間長,或者是php-cgi進程死掉;已經fastCGI使?情況等都會導致502、504錯誤。
b、502 是指請求的php-fpm已經執?,但是由于某種原因?沒有執?完畢,最終導致php-fpm進程終?。?般來說,與php-fpm.conf的設置有關,也與php的執?程序性能有關,?站的訪問量?,?php-cgi的進程數偏少。針對這種情況的502錯誤,只需增加 php-cgi的進程數。具體就是修改/usr/local/php/etc/ php-fpm.conf?件,將其中的max_children值適當增加。這個數據要依據你的服務器的配置進?設置。?般?個php-cgi進程占20M內存,你可以??計算下,適量增多。
/usr/local/php/sbin/php-fpm reload 然后重啟?下.
c、504 表?超時,也就是客戶端所發出的請求沒有到達?關,請求沒有到可以執?的php-fpm。與nginx.conf的配置也有關系。
501 服務器不具備完成請求的功能。例如,服務器?法識別請求?法時可能會返回此代碼。
503 服務器?前?法使?(由于超載或停機維護)。通常,這只是暫時狀態。(服務不可?)
505 服務器不?持請求中所?的 HTTP 協議版本。(HTTP 版本不受?持)
附:新增全部狀態碼含義
狀態碼分類
整體范圍 已定義范圍 分類
100~199 100~101 信息提?
200~299 200~206 成功
300~399 300~305 重定向
400~499 400~415 客戶端錯誤
500~599 500~505 服務器錯誤
100(continue):說明收到了請求的初始部分,請客戶端繼續。
客戶端發送?個攜帶值為100 Countinue的Expect請求?部,意味著客戶端在發送實體前等待100 Continue響應。這是?種優化,客戶端在避免向服務器發送?個?實體時,才使?。
101(Switching Protocols):說明服務器正在根據客戶端的指定,將協議切換成Update?部所列的協議。
200(OK):請求沒問題,實體的主體部分包含了所請求的資源。
201(Created):?于創建服務器對象的請求的響應(例如PUT)。實體主體部分包含各種引?了已創建的資源的URL。
202(Accepted):請求已被接受,但服務器還未對其執?任何動作,?法保證服務器會完成這個請求。
203(Non-Authoritative Information):實體?部包含的信息不是來?于源端服務器,?是來?資源的?份副本。
204(No Content):響應報?有狀態?和?部,但沒有實體的主體部分。
205(Reset Content):告知瀏覽器清除當前頁?中的所有HTML表單元素。
206(Partial Content):成功執?了?個部分或Range(范圍)請求。
重定向狀態碼要么告知客戶端使?替代位置來訪問他們所要訪問的資源,要么提供?個替代的響應?不是資源的內容。
資源被移動的情況下,發送?個重定向狀態碼和?個可選的Location?部告知客戶端資源已被移動,并且可以在哪?找到。
300(Multiple Choices):客戶端請求?個實際指向多個資源的URL時會返回這個狀態碼。
301(Moved Permanently):請求的URL已經被移除,響應中Location?部包含資源現所處的URL。
302(Found):與301類似,但是客戶端應該?Location?部的URL來臨時定位資源,將來的請求仍??的URL。
303(See Other):告知客戶端應該?另?個URL來獲取資源,新URL位于Location?部,允許POST請求的響應將客戶端定向到某個資源上去。
304(Not Modified):客戶端可以通過所包含的請求?部,使其變成有條件的。
305(Use Proxy):說明必須通過?個代理來訪問資源,代理位置由Location?部給出。
306(未使?)
307(Temporary Redirect):和302?樣。
400(Bad Request):?于告知客戶端它發送了?個錯誤的請求。
401(Unauthorized):與適當的?部?起返回,在這些?部中請求客戶端在獲取對資源的訪問權之前,對??進?認證。
402(Payment Required):保留狀態碼,未來之?。
403(Forbidden):說明請求被訪問權拒絕。拒絕原因可能在實體的主體部分。
404(Not Found):服務器?法找到所請求的URL。
405(Method Not Allowed):發起請求中帶有所請求的URL不?持的?法。響應中有Allow?部,告知客戶端對所請求的資源可以?哪些?法。
406(Not Acceptable):客戶端可以通過參數說明它們接受什么類型的實體,服務器沒有和客戶端可接受的URL相匹配的資源時?此代碼。
407(Proxy Authentication Required):和401相似,但?于要求對資源進?認證的代理服務器。
408(Request Timeout):如果完成請求所花時間太長,服務器可以返回此狀態碼。
409(Confict):說明請求可能在資源上引發的?些沖突。
410(Gone):與404類似,只是服務器之前有過此資源。
411(Length Required):服務器要求在請求報?中包含Content-Length?部。
412(Precondition Failed):客戶端發起了條件請求,且其中?個條件失敗了。
413(Request Entity Too Large):實體主體部分過?。
414(Request URI Too Long):URL?服務器能處理的長。
415(Unsupport Media Type):?法理解或?法?持客戶端所發實體的內容類型。
416(Requested Range Not Satisfiable):請求報?請求的是指定資源的某個范圍,?此范圍?效或?法滿?。
417(Expectation Failed):請求的Expect請求?部包含?個期望,但服務器?法滿?此期望。
500(Internal Server Error):服務器遇到?個妨礙它為請求提供服務的錯誤。
501(Not Implemented):客戶端發起的請求超出服務器的能?范圍。
502(Bad Gateway):作為代理或?關使?的服務器從請求響應鏈的下?條鏈路上收到了?條偽響應。
503(Service Unavailable):說明服務器現在?法為請求提供服務,將來可以。
504(Gateway Timeout):與408相似。
505(HTTP Version Not Support):服務器收到的請求使?了它?法或不愿意?持的協議版本。
現在各大技術社區 Spring Boot 的文章越來越多,Spring Boot 相關的圖文、視頻教程越來越多,使用 Spring Boot 的互聯網公司也越來越多; Java 程序員現在出去面試, Spring Boot 已經成了必問的內容。
一切都在證明,Spring Boot 已經成為了 Java 程序員必備的技能。并且可以預見的是未來 Spring Boot 的發展還會更好。
所以對Java程序員來說其中不乏說對 Spring Boot 非常熟悉的,然后當問到一些 Spring Boot 核心功能和原理的時候,沒人能說得上來,或者說不到點上,可以說一個問題就問趴下了!(問題:你能講下為什么我們要用 Spring Boot 嗎?)
相信我,上面這些類似的問題,90%有經驗的Java程序員超都曾遇見過!但很少有系統化的回答。
因此,總結了這份Spring Boot核心知識點實戰教程,通過這份教程,帶你梳理Spring Boot 技術體系。
文末有彩蛋~
Spring Boot2教程在Spring Boot項目中,正常來說是不存在XML配置,這是因為Spring Boot不推薦使用 XML ,注意,并非不支持,Spring Boot 推薦開發者使用 Java 配置來搭建框架,Spring Boot 中,大量的自動化配置都是通過 Java 配置來實現的,這一套實現方案,我們也可以自己做,即自己也可以使用純 Java 來搭建一個 SSM 環境,即在項目中,不存在任何 XML 配置,包括 web.xml 。
環境要求:
使用純 Java 來搭建 SSM 環境,要求 Tomcat 的版本必須在 7 以上。
創建一個普通的 Maven工程(注意,這里可以不必創建Web工程),并添加SpringMVC的依賴,同時,這里環境的搭建需要用到 Servlet ,所以我們還需要引入 Servlet 的依賴(一定不能使用低版本的Servlet),最終的 pom.xml 文件如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
工程創建成功之后,首先添加 Spring 的配置文件,如下:
@Configuration
@ComponentScan(basePackages="org.javaboy", useDefaultFilters=true,
excludeFilters={@ComponentScan.Filter(type=FilterType.ANNOTATION, classes=Controller.class)})
public class SpringConfig {
}
關于這個配置,我說如下幾點:
@Configuration 注解表示這是一個配置類,在我們這里,這個配置的作用類似于applicationContext.xml
@ComponentScan 注解表示配置包掃描,里邊的屬性和 xml 配置中的屬性都是一一對應的,useDefaultFilters 表示使用默認的過濾器,然后又除去 Controller 注解,即在 Spring 容器中掃描除了 Controller 之外的其他所有 Bean 。
接下來再來創建 springmvc 的配置文件:
@Configuration
@ComponentScan(basePackages="org.javaboy",useDefaultFilters=false,includeFilters={@ComponentScan.Filter(type=FilterType.ANNOTATION,classes=Controller.class)})
public class SpringMVCConfig {
}
注意,如果不需要在SpringMVC中添加其他的額外配置,這樣就可以了。即視圖解析器、JSON解析、文件上傳…等等,如果都不需要配置的話,這樣就可以了。
此時,我們并沒 web.xml 文件,這時,我們可以使用Java代碼去代替 web.xml 文件,這里會用到WebApplicationInitializer ,具體定義如下:
public class WebInit implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException
{
//首先來加載 SpringMVC 的配置文件
AnnotationConfigWebApplicationContext ctx=new
AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfig.class);
// 添加 DispatcherServlet
ServletRegistration.Dynamic springmvc=servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
// 給 DispatcherServlet 添加路徑映射
springmvc.addMapping("/");
// 給 DispatcherServlet 添加啟動時機
springmvc.setLoadOnStartup(1);
}
}
WebInit 的作用類似于 web.xml,這個類需要實現 WebApplicationInitializer 接口,并實現接口中的方法,當項目啟動時,onStartup 方法會被自動執行,我們可以在這個方法中做一些項目初始化操作,例如加載 SpringMVC 容器,添加過濾器,添加 Listener、添加 Servlet 等。
注意:
由于我們在WebInit中只是添加了SpringMVC的配置,這樣項目在啟動時只會去加載SpringMVC容器,而不會去加載 Spring 容器,如果一定要加載 Spring 容器,需要我們修改 SpringMVC 的配置,在SpringMVC 配置的包掃描中也去掃描 @Configuration 注解,進而加載 Spring 容器,還有一種方案可以解決這個問題,就是直接在項目中舍棄 Spring 配置,直接將所有配置放到 SpringMVC 的配置中來完成,這個在 SSM 整合時是沒有問題的,在實際開發中,較多采用第二種方案,第二種方案,SpringMVC 的配置如下:
@Configuration
@ComponentScan(basePackages="org.javaboy")
public class SpringMVCConfig {
}
這種方案中,所有的注解都在 SpringMVC 中掃描,采用這種方案的話,則 Spring 的配置文件就可以刪除了。
最后,添加一個 HelloController ,然后啟動項目進行測試:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
啟動項目,訪問接口,結果如下:
Spring Boot全局異常處理
在Spring Boot項目中 ,異常統一處理,可以使用Spring中@ControllerAdvice來統一處理,也可以自己來定義異常處理方案。Spring Boot 中,對異常的處理有一些默認的策略,我們分別來看。
默認情況下,Spring Boot 中的異常頁面 是這樣的:
我們從這個異常提示中,也能看出來,之所以用戶看到這個頁面,是因為開發者沒有明確提供一個/error 路徑,如果開發者提供了 /error 路徑 ,這個頁面就不會展示出來,不過在 Spring Boot 中,提供/error 路徑實際上是下下策,Spring Boot本身在處理異常時,也是當所有條件都不滿足時,才會去找 /error 路徑。那么我們就先來看看,在 Spring Boot 中,如何自定義 error 頁面,整體上來說,可以分為兩種,一種是靜態頁面,另一種是動態頁面。
自定義靜態異常頁面,又分為兩種,第一種 是使用HTTP響應碼來命名頁面,例如404.html、405.html、500.html …,另一種就是直接定義一個 4xx.html,表示400-499 的狀態都顯示這個異常頁面,5xx.html 表示 500-599 的狀態顯示這個異常頁面。
默認是在 classpath:/static/error/ 路徑下定義相關頁面:
此時,啟動項目,如果項目拋出 500 請求錯誤,就會自動展示 500.html 這個頁面,發生 404 就會展示404.html 頁面。如果異常展示頁面既存在 5xx.html,也存在 500.html ,此時,發生500異常時,優先展示 500.html 頁面。
動態異常頁面
動態的異常頁面定義方式和靜態的基本 一致,可以采用的頁面模板有 jsp、freemarker、thymeleaf。
動態異常頁面,也支持 404.html 或者 4xx.html ,但是一般來說,由于動態異常頁面可以直接展示異常詳細信息,所以就沒有必要挨個枚舉錯誤了 ,直接定義 4xx.html(這里使用thymeleaf模板)或者5xx.html 即可。
注意,動態頁面模板,不需要開發者自己去定義控制器,直接定義異常頁面即可 ,Spring Boot 中自帶的異常處理器會自動查找到異常頁面。
頁面定義如下:
頁面內容如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>5xx</h1>
<table border="1">
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
</table>
</body>
</html>
默認情況下,完整的異常信息就是這5條,展示 效果如下 :
如果動態頁面和靜態頁面同時定義了異常處理頁面,例如 classpath:/static/error/404.html 和classpath:/templates/error/404.html 同時存在時,默認使用動態頁面。即完整的錯誤頁面查找
方式應該是這樣:
發生了 500 錯誤–>查找動態 500.html 頁面–>查找靜態 500.html --> 查找動態 5xx.html–>查找靜態5xx.html。
自定義異常數據
默認情況下,在 Spring Boot 中,所有的異常數據其實就是上文所展示出來的 5 條數據,這 5 條數據定義在 org.springframework.boot.web.reactive.error.DefaultErrorAttributes 類中,具體定義在 getErrorAttributes 方法中 :
public Map<String, Object> getErrorAttributes(ServerRequest request,
boolean includeStackTrace) {
Map<String, Object> errorAttributes=new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.path());
Throwable error=getError(request);
HttpStatus errorStatus=determineHttpStatus(error);
errorAttributes.put("status", errorStatus.value());
errorAttributes.put("error", errorStatus.getReasonPhrase());
errorAttributes.put("message", determineMessage(error));
handleException(errorAttributes, determineException(error),
includeStackTrace);
return errorAttributes;
}
DefaultErrorAttributes 類本身則是在
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 異常自動配置類中定義的,如果開發者沒有自己提供一個 ErrorAttributes 的實例的話,那么 Spring Boot 將自動提供一個 ErrorAttributes 的實例,也就是 DefaultErrorAttributes 。
基于此 ,開發者自定義 ErrorAttributes 有兩種方式 :
具體定義如下:
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean
includeStackTrace) {
Map<String, Object> map=super.getErrorAttributes(webRequest,
includeStackTrace);
if ((Integer)map.get("status")==500) {
map.put("message", "服務器內部錯誤!");
}
return map;
}
}
定義好的 ErrorAttributes 一定要注冊成一個 Bean ,這樣,Spring Boot 就不會使用默認的DefaultErrorAttributes 了,運行效果如下圖:
自定義異常視圖
異常視圖默認就是前面所說的靜態或者動態頁面,這個也是可以自定義的,首先 ,默認的異常視圖加載邏輯在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 類的errorHtml 方法中,這個方法用來返回異常頁面+數據,還有另外一個 error 方法,這個方法用來返回異常數據(如果是 ajax 請求,則該方法會被觸發)。
@RequestMapping(produces=MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status=getStatus(request);
Map<String, Object> model=Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request,
MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView=resolveErrorView(request, response, status,
model);
return (modelAndView !=null) ? modelAndView : new ModelAndView("error",
model);
}
在該方法中 ,首先會通過 getErrorAttributes 方法去獲取異常數據(實際上會調用到 ErrorAttributes的實例 的 getErrorAttributes 方法),然后調用 resolveErrorView 去創建一個 ModelAndView ,如果這里創建失敗,那么用戶將會看到默認的錯誤提示頁面。
正常情況下, resolveErrorView 方法會來到 DefaultErrorViewResolver 類的 resolveErrorView 方法中:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
status,
Map<String, Object> model) {
ModelAndView modelAndView=resolve(String.valueOf(status.value()),
model);
if (modelAndView==null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView=resolve(SERIES_VIEWS.get(status.series()),
model);
}
return modelAndView;
}
在這里,首先以異常響應碼作為視圖名分別去查找動態頁面和靜態頁面,如果沒有查找到,則再以 4xx或者 5xx 作為視圖名再去分別查找動態或者靜態頁面。
要自定義異常視圖解析,也很容易 ,由于 DefaultErrorViewResolver 是在ErrorMvcAutoConfiguration 類中提供的實例,即開發者沒有提供相關實例時,會使用默認的DefaultErrorViewResolver ,開發者提供了自己的 ErrorViewResolver 實例后,默認的配置就會失效,因此,自定義異常視圖,只需要提供 一個 ErrorViewResolver 的實例即可:
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext,
ResourceProperties resourceProperties) {
super(applicationContext, resourceProperties);
}
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
status, Map<String, Object> model) {
return new ModelAndView("/aaa/123", model);
}
}
實際上,開發者也可以在這里定義異常數據(直接在 resolveErrorView 方法重新定義一個 model ,將參數中的model 數據拷貝過去并修改,注意參數中的 model 類型為 UnmodifiableMap,即不可以直接修改),而不需要自定義 MyErrorAttributes。定義完成后,提供一個名為 123 的視圖,如下圖:
如此之后,錯誤試圖就算定義成功了。
實際上也可以自定義異??刂破?BasicErrorController ,不過我覺得這樣太大動干戈了,沒必要,前面幾種方式已經可以滿足我們的大部分開發需求了。如果是前后端分離架構,異常處理還有其他一些處理方案,這個以后和大家聊。
篇幅有限,其他內容就不在這里一一展示了,這份Spring Boot實戰教程已整理成一份PDF文檔,共有200多頁。
關注公眾號:程序零世界,回復 666 獲取這份整理好的Spring Boot實戰教程。
歡迎大家一起交流,喜歡文章記得點ge 贊喲,感謝支持!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。