- 第 1 章 SpringBoot 入門
- 1.1 Spring Boot 簡介
- 1.2 微服務(wù)
- 1.3 環(huán)境準(zhǔn)備
- 1.4 SpringBoot HelloWorld
- 1.5 探究 HelloWorld
- pom 文件
- 導(dǎo)入依賴
- Main 主程序類
- 啟動日志分析
- 1.6 快速創(chuàng)建 SpringBoot 項(xiàng)目
- 第 2 章 SpringBoot 配置
- 2.1 配置文件
- 2.2 YAML 語法
- 基本語法
- 值的寫法
- 配置屬性列表
- 2.3 配置文件值的注入
- @ConfigurationProperties 配置文件屬性的綁定
- 松散綁定
- Properties 亂碼問題
- @ConfigurationProperties 與 @Value
- @PropertySource 與 @ImportResource
- @Configuration 和 @Bean
- 2.3 配置文件占位符
- 屬性配置占位符
- 隨機(jī)數(shù)
- 2.4 Profile 多環(huán)境配置文件切換
- 2.5 配置文件加載位置
- 2.6 配置文件加載順序(重點(diǎn))
- 2.7 自動配置原理(重難點(diǎn))
- 2.8 @Conditional 自動配置報(bào)告
- 第 3 章 SpringBoot 日志
- 3.1 常見日志框架
- 3.2 SLF4j 使用
- 3.3 SpringBoot 日志關(guān)系(了解)
- 3.4 日志使用
- 常見日志配置屬性
- 指定日志框架配置文件
- 3.5 切換日志框架
- 第 4 章 SpringBoot 與 WEB 開發(fā)
4.1 簡介
-
4.2 SpringBoot 靜態(tài)資源映射規(guī)則
- Webjars
-
4.3 模板引擎
- 引入Thymeleaf
- Thymeleaf 的使用
- Thymeleaf 語法
4.4 SpringMVC 自動配置
4.5 修改 SpringBoot 默認(rèn)配置
-
4.6 Restful crud 項(xiàng)目
-
- 國際化
- 國際化原理
- 實(shí)現(xiàn)語言切換功能
- 登錄
- 顯示員工列表
- 跳轉(zhuǎn)到員工頁面
- 添加員工
- 重定向與轉(zhuǎn)發(fā)
- 日期格式化
- 跳轉(zhuǎn)到員工編輯頁面
- 編輯員工信息
- 員工刪除
-
-
4.7 定制錯誤頁面
- SpringBoot 默認(rèn)錯誤處理機(jī)制
-
4.8 配置嵌入式 Servlet 容器
- 修改 Servlet 容器的相關(guān)配置
- 使用其他嵌入式 Servlet 容器
4.9 嵌入式 Servlet 容器自動配置原理
4.10 嵌入式 Servlet 容器啟動原理
4.11 使用外置的 Servlet 容器
- 第 5 章 SpringBoot 與 Docker
- 5.1 Docker 簡介
- 5.2 核心概念
- 5.3 安裝Docker
- 安裝linux虛擬機(jī)
- 在linux虛擬機(jī)上安裝docker
- 5.4 Docker常用命令&操作
- 鏡像操作
- 容器操作
- 安裝 MySQL 示例
- 第 6 章 SpringBoot 與數(shù)據(jù)訪問
- 6.1 數(shù)據(jù)源初始化與 JDBC
- 配置 MySQL
- 數(shù)據(jù)源自動配置原理
- 數(shù)據(jù)表自動初始化
- 使用 JdbcTemplate 查詢數(shù)據(jù)
- 數(shù)據(jù)庫自動初始化原理
- 6.2 使用外部數(shù)據(jù)源
- 6.3 自定義數(shù)據(jù)源原理
- 6.4 配置 Druid 數(shù)據(jù)源
- 6.5 整合 MyBatis
- 注解版
- Mybatis 常見配置
- xml 版
- 6.6 整合 SpringData JPA
- Spring Data 簡介
- 整合 SpringData JPA
- 6.1 數(shù)據(jù)源初始化與 JDBC
- 第 7 章 SpringBoot 啟動配置原理
- 7.1 啟動流程
- 創(chuàng)建SpringApplication對象
- 運(yùn)行run方法
- 7.2 事件監(jiān)聽機(jī)制
- 7.1 啟動流程
- 第 8 章 SpringBoot 自定義 starter
- 8.1 starter 原理
- 8.2 自定義 starter
- SpringBoot 與開發(fā)熱部署
- 進(jìn)階學(xué)習(xí)
- 待補(bǔ)充
- 推薦閱讀
- 參考文檔
你無法掌握所有的知識,抓大放小憔古,不要關(guān)注邊邊角角的知識,保證最重要的知識爛熟于胸即可 —— 羅翔
第 1 章 SpringBoot 入門
1.1 Spring Boot 簡介
簡化Spring應(yīng)用開發(fā)的一個框架;
整個Spring技術(shù)棧的一個大整合扬虚;
J2EE開發(fā)的一站式解決方案丙猬;
傳統(tǒng)的 Spring 開發(fā)需要經(jīng)歷以下步驟:
- 配置 pom.xml,引入 SSM 項(xiàng)目各種依賴
- 配置
web.xml
实蔽,設(shè)置監(jiān)聽器ContextLoaderListener
卷扮,當(dāng)項(xiàng)目啟動時自動啟動 Spring 容器 - 配置
web.xml
荡澎,設(shè)置 Spring 的配置文件applicationContext.xml
- 配置
web.xml
,設(shè)置 SpringMVC 前端控制器DispatcherServlet
攔截所有請求 - 配置
web.xml
晤锹,設(shè)置字符集編碼過濾器CharacterEncodingFilter
摩幔,隱藏HTTP請求過濾器HiddenHttpMethodFilter
- 配置
applicationContext.xml
,加載 properties 配置文件鞭铆,設(shè)置 Spring 自動掃描的包component-scan
- 配置
applicationContext.xml
或衡,設(shè)置數(shù)據(jù)源,數(shù)據(jù)庫連接信息和相關(guān)屬性 - 配置
applicationContext.xml
,和 Mybatis 整合封断,設(shè)置SqlSessionFactory
Bean斯辰,設(shè)置 Mybatis 掃描的接口,會生成代理類加入容器 - 配置
dispatcherServlet-servlet.xml
坡疼,設(shè)置 SpringMVC 視圖解析器InternalResourceViewResolver
- 配置
dispatcherServlet-servlet.xml
椒涯,設(shè)置注解驅(qū)動,設(shè)置上傳文件的beanCommonsMultipartResolver
- 對數(shù)據(jù)源配置事務(wù)管理器
DataSourceTransactionmanager
回梧,開啟基于注解的事務(wù) - 配置
pom.xml
,開發(fā)環(huán)境配置 Jetty Maven 插件祖搓,使用jetty:run
啟動項(xiàng)目 - 項(xiàng)目發(fā)布需要打 war 包狱意,配置服務(wù)器環(huán)境,包括 tomcat拯欧,MySQL等详囤。
[圖片上傳失敗...(image-927ff1-1606212213488)]
具體配置參考項(xiàng)目 spring-boot-01-ssm
,更多相關(guān)參考 SSM整合教程镐作,
繁多的配置藏姐,低下的開發(fā)效率,復(fù)雜的部署流程该贾,第三方技術(shù)集成難度大羔杨,SpringBoot 是為了簡化 Spring 應(yīng)用開發(fā)而生,具有以下優(yōu)點(diǎn):
- 快速創(chuàng)建 Spring 項(xiàng)目杨蛋,快速與主流框架集成
- 去除了 Spring 中繁瑣的 XML 配置兜材,開箱即用,以前需要配置 web.xml逞力、applicationContext.xml等文件才可以使用
- 使用嵌入式 Tomcat 容器曙寡,無需打成 war 包,幫助快速開發(fā)和部署
- starters 自動依賴管理與版本控制寇荧,比如要使用 WEB 工程举庶,導(dǎo)入 WEB starters 即可,WEB 依賴的其他 jar 包揩抡,starters 會自動導(dǎo)入依賴并控制版本户侥,避免了版本不兼容和依賴繁瑣的問題
- 大量的自動配置,簡化開發(fā)捅膘。以前創(chuàng)建一個項(xiàng)目添祸,要懂得 SpringMVC Mybatis 如何配置
- 準(zhǔn)生產(chǎn)環(huán)境的運(yùn)行時應(yīng)用監(jiān)控
- 與云計(jì)算的天然集成
1.2 微服務(wù)
2014,martin fowler 在博客中提出了微服務(wù)概念
微服務(wù):架構(gòu)風(fēng)格(服務(wù)微化)
一個應(yīng)用應(yīng)該是一組小型服務(wù)寻仗;
可以通過HTTP的方式進(jìn)行互通刃泌;(RPC?)
單體應(yīng)用:ALL IN ONE,所有服務(wù)都在一個應(yīng)用中,不方便擴(kuò)展耙替,一處錯誤可能影響整個應(yīng)用使用
微服務(wù):每一個功能元素最終都是一個可獨(dú)立替換和獨(dú)立升級的軟件單元亚侠;
1.3 環(huán)境準(zhǔn)備
- jdk1.8,Spring Boot 推薦jdk1.7及以上
- Apache Maven 3.3.9
- IntelliJIDEA2017
- SpringBoot 1.5.9.RELEASE
1.4 SpringBoot HelloWorld
實(shí)現(xiàn)一個功能:瀏覽器發(fā)送 hello 請求俗扇,服務(wù)器接收請求并處理硝烂,返回 Hello SpringBoot 字符串。
傳統(tǒng) Spring 開發(fā): 進(jìn)行 Spring铜幽、SpringMVC 各種 xml 配置滞谢,然后進(jìn)行開發(fā),最后打成 war 包除抛,放入 Tomcat 后運(yùn)行狮杨。
SpringBoot 開發(fā):
- 創(chuàng)建一個 Maven 項(xiàng)目
- 引入 SpringBoot 依賴
<!-- 繼承SpringBoot父項(xiàng)目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- 添加SpringBoot web啟動器依賴,不需要設(shè)置版本到忽,在父項(xiàng)目中已經(jīng)添加 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 編寫一個主程序橄教,啟動 SpringBoot 應(yīng)用
/**
* @SpringBootApplication 來標(biāo)注一個主程序類,說明這是一個 SpringBoot 應(yīng)用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// 啟動Spring應(yīng)用
SpringApplication.run(HelloWorldMainApplication.class, args);
}
}
- 開發(fā):直接編寫 Controller 與 Service喘漏,不需要配置 Spring 與 SpringMVC 了
@Controller
public class HelloContorller {
@RequestMapping("/hello") // 設(shè)置訪問的url
@ResponseBody // 表示數(shù)據(jù)直接返回給瀏覽器护蝶,如果是對象則轉(zhuǎn)為json數(shù)據(jù)
public String hello() {
return "Hello SpringBoot...";
}
}
- 測試:直接啟動主程序 Main 即可,不需要打包到 Tomcat 啟動
訪問 http://localhost:8080/hello 翩迈,顯示 Hello SpringBoot...
-
部署:
- 引入
Maven SpringBoot
插件
<!-- 這個插件持灰,將應(yīng)用打包成一個可執(zhí)行的jar包,用于項(xiàng)目部署時使用帽馋,會包含項(xiàng)目所有依賴的Jar包搅方,包括Tomcat*.jar,Sprint-aop.jar等--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
-
maven package
打 jar 包绽族,在/target
下生成spring-boot-01-helloworld-1.0-SNAPSHOT.jar
姨涡,可以在 jar 包\BOOT-INF\lib
目錄下看到項(xiàng)目所依賴的 jar,包括 log4j.jar吧慢,spring-aop.jar涛漂,spring-beans.jar,spring-webmvc.jar检诗,tomcat.jar 等匈仗;\BOOT-INF\classes
下為我們的源碼。 -
java -jar spring-boot-01-helloworld-1.0-SNAPSHOT.jar
在生產(chǎn)環(huán)境 Cmd 命令行啟動該項(xiàng)目逢慌,可以正常訪問悠轩,不需要部署Tomcat環(huán)境。
- 引入
注意: 如果不引入Maven SpringBoot
插件攻泼,使用 Maven 打包雖然也能生成 jar火架,但是不包含項(xiàng)目依賴的jar包鉴象,測試環(huán)境部署會出現(xiàn)錯誤。
1.5 探究 HelloWorld
1. pom 文件
SpringBoot 項(xiàng)目都需要繼承一個父項(xiàng)目 spring-boot-starter-parent何鸡,雙擊可以進(jìn)入 spring-boot-starter-parent.pom 文件
<!-- SpringBoot項(xiàng)目的父項(xiàng)目纺弊,父項(xiàng)目一般用來做依賴管理 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<!-- 上項(xiàng)目的父項(xiàng)目是spring-boot-dependencies -->
<!-- 用來管理 SpringBoot 項(xiàng)目中所有依賴的版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
spring-boot-dependencies.pom 中設(shè)置了各種依賴的版本 version,包括 aspect.version骡男,log4j.version淆游,commons-dbcp.version,servlet-api.version隔盛,所以 SpringBoot 項(xiàng)目依賴一般不需要聲明版本version犹菱,這就是前文中所說的 SpringBoot 自動依賴管理與版本控制功能。
2. 導(dǎo)入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter:SpringBoot場景啟動器吮炕;幫我們導(dǎo)入了 web 模塊運(yùn)行所依賴的組件已亥,包括 tomcat,jackson来屠,webmvc 等。如果沒有 starter 啟動器震鹉,我們就需要手動導(dǎo)入依賴的組件并設(shè)置版本俱笛,這個操作非常麻煩且容易出錯。
SpringBoot 將所有的功能場景都抽取成 starter 啟動器传趾,比如需要 aop 功能就導(dǎo)入 spring-boot-starter-aop 依賴迎膜,需要 redis 功能就導(dǎo)入 spring-boot-starter-data-redis 依賴,相關(guān)場景的所有依賴都會導(dǎo)入進(jìn)來浆兰。
3. Main 主程序類
/**
* @SpringBootApplication 來標(biāo)注一個主程序類磕仅,說明這是一個 SpringBoot 應(yīng)用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// 啟動Spring應(yīng)用
SpringApplication.run(HelloWorldMainApplication.class, args);
}
}
-
@SpringBootApplication
:標(biāo)注SpringBoot的的主配置類,SpringBoot 就應(yīng)該運(yùn)行這個類的 main 方法來啟動 SpringBoot 應(yīng)用下面是作用相同的使用注解創(chuàng)建容器的 Spring 代碼簸呈,@Configuration 的作用是設(shè)置配置類榕订,在創(chuàng)建容器時傳入被標(biāo)記的配置類
Application
,相當(dāng)于之前的new ClassPathXmlApplicationContext("spring-config.xml")
蜕便,@Configuration // 標(biāo)注該類為配置類, 與 spring-config.xml 作用類似 @ComponentScan("com.imooc") public class Application { public static void main(String[] args) { // 1. 啟動容器, 解析配置類, 掃描注解標(biāo)記的 Bean 到容器 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class); // 2. 從容器中獲取 Bean WelcomService welcomeService = (WelcomService) applicationContext.getBean("welcomeService"); welcomeService.sayHello("spring..."); }
@SpringBootApplication 是一個組合注解劫恒,包括 @SpringBootConfiguration,而該注解底層是 Spring @Configuration轿腺,即 Spring 的配置類注解两嘴,類似于配置文件,@Configuration 底層是 @Component族壳,也是 Spring 的一個組件憔辫,會被 Spring 掃描加入到容器中。
@SpringBootApplication 替代了 @Configuration + @ComponentScan仿荆,在 main 方法中傳入被標(biāo)記的類作為配置類贰您,該注解源碼如下:
// SpringBoot配置類坏平,自動配置注解 @SpringBootConfiguration @EnableAutoConfiguration // 設(shè)置為\掃描的包為標(biāo)記類所在的包 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter(type = FilterType.CUSTOM, classes ={AutoConfigurationExcludeFilter.class})}) public @interface SpringBootApplication {
@EnableAutoConfiguration
:開啟自動配置功能;
以前我們需要配置的東西枉圃,SpringBoot幫我們自動配置功茴,該注解就是告訴 SpringBoot 開啟自動配置功能。是一個組合注解孽亲,源碼如下:
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
-
@AutoConfigurationPackage
:自動配置包坎穿,將主配置類(@SpringBootApplication標(biāo)注的類)所在包下及子包里的所有組件都掃描添加到 Spring 容器,這也是為什么我們配有配置掃描包 component-scan返劲,依然能夠注入所有 Component 組件的原因底層是 Spring @Import玲昧,作用是給容器中導(dǎo)入一個組件。類似于 @Componet
為了驗(yàn)證 @AutoConfigurationPackage 是掃描主配置類所在包下及子包里的所有組件到 Spring 容器篮绿,所以我們在其他包下創(chuàng)建一個 Controller孵延,驗(yàn)證發(fā)現(xiàn)確實(shí)不會被掃描到。
上述注解@Import亲配,@AutoConfigurationPackage 的底層都是 Spring 的注解尘应,參考Spring注解驅(qū)動教程
-
EnableAutoConfigurationImportSelector
:導(dǎo)入哪些組件的選擇器;Spring Boot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值吼虎,將這些值作為自動配置類導(dǎo)入到容器中犬钢,自動配置類就生效,幫我們進(jìn)行自動配置工作思灰;
4. 啟動日志分析
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.9.RELEASE) 說明SpringBoot版本
: Starting HelloWorldMainApplication on DESKTOP-TDM8SAD with PID 15920 # 項(xiàng)目啟動玷犹,進(jìn)程ID
: No active profile set, falling back to default profiles: default # 生效的配置文件profile
: Tomcat initialized with port(s): 8080 (http) # Tomcat的訪問端口
: Starting service [Tomcat]
: Starting Servlet Engine: Apache Tomcat/8.5.23
: Initializing Spring embedded WebApplicationContext
: Root WebApplicationContext: initialization completed in 1297 ms
: Mapping servlet: 'dispatcherServlet' to [/] # dispatcherServlet 攔截所有請求
: Mapping filter: 'characterEncodingFilter' to: [/*] # 字符集編碼過濾器
: Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
: Mapping filter: 'httpPutFormContentFilter' to: [/*]
: Mapping filter: 'requestContextFilter' to: [/*]
: Looking for @ControllerAdvice: org.springframework.boot.context.embedded.
: Mapped "{[/hello]}" onto public String com.atguigu.controller.HelloContorller.hello() # 映射用戶編寫的Controller
: Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity # 映射/error頁面
: Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
: Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
: Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
: Registering beans for JMX exposure on startup
: Tomcat started on port(s): 8080 (http) # Tomcat啟動完成,端口為8080
: Started HelloWorldMainApplication in 2.047 seconds (JVM running for 2.35) # HelloWorldMainApplication項(xiàng)目啟動完成洒疚,耗時2.0秒
1.6 快速創(chuàng)建 SpringBoot 項(xiàng)目
在 IDEA 中安裝 Spring Assistant 插件歹颓,然后新建項(xiàng)目:
- 選擇 Spring Assistant,選擇 Default 即從 Spring 官網(wǎng)(https://start.spring.io/ )聯(lián)網(wǎng)創(chuàng)建 SpringBoot項(xiàng)目油湖,
- 填寫項(xiàng)目名稱信息
- 選擇需要導(dǎo)入的依賴巍扛,如 web,MySQL 等乏德,還會自動引入 Maven SpringBoot 打包插件电湘。
這樣就很快捷的創(chuàng)建了一個 SpringBoot 項(xiàng)目,這種創(chuàng)建方式需要聯(lián)網(wǎng)鹅经,創(chuàng)建成功后查看主程序與 POM 文件寂呛,發(fā)現(xiàn)與手動創(chuàng)建完全一致。
默認(rèn)生成的Spring Boot項(xiàng)目瘾晃;
- 主程序已經(jīng)生成好了贷痪,我們只需要寫自己的業(yè)務(wù)邏輯
- resources文件夾中目錄結(jié)構(gòu) static:保存所有的靜態(tài)資源; js css images蹦误;
- templates:保存所有的模板頁面劫拢;(Spring Boot默認(rèn)jar包使用嵌入式的Tomcat肉津,默認(rèn)不支持JSP頁 面);可以使用模板引擎(freemarker舱沧、thymeleaf)妹沙;
- application.properties:Spring Boot應(yīng)用的配置文件;可以修改一些默認(rèn)設(shè)置熟吏,如Tomcat端口距糖;
補(bǔ)充: @ResponseBody 注解表示返回字符串,對象則轉(zhuǎn)為json數(shù)據(jù)
//@ResponseBody // 表示數(shù)據(jù)直接返回給瀏覽器牵寺,如果是對象則轉(zhuǎn)為json數(shù)據(jù)
//@Controller
@RestController // 等價于@ResponseBody + @Controller悍引,可以通過@RestController源碼查看
public class HelloController {
@RequestMapping("/hello") // 設(shè)置訪問的url
public String hello() {
return "Hello SpringBoot quickly...";
}
// RESTAPI的方式,即把返回?cái)?shù)據(jù)直接發(fā)送給瀏覽器帽氓,而不是頁面跳轉(zhuǎn)
}
第 2 章 SpringBoot 配置
2.1 配置文件
SpringBoot 使用一個全局的配置文件趣斤,文件名稱默認(rèn)為
application.properties
application.yml
配置文件的作用:修改 SpringBoot 自動配置的默認(rèn)值,如修改訪問端口黎休,修改項(xiàng)目根路徑 Context-path
YAML 適合用來做配置文件浓领,替換我們以前使用的 xml 配置文件:
server:
port: 8081
傳統(tǒng)的 XML 配置如下,不禁冗長势腮,還容易出現(xiàn)標(biāo)簽筆誤:
<server>
<prot>8081</port>
</server>
2.2 YAML 語法
1. 基本語法
key: value
表示一對鍵值對镊逝,大小寫敏感,注意冒號后必須有空格
以空格的縮進(jìn)來控制層級關(guān)系嫉鲸,左對齊的一列數(shù)據(jù)都是同一層級
2. 值的寫法
字面量:
普通的值(數(shù)字,字符串歹啼,布爾) k: v:字面直接來寫玄渗;
字符串默認(rèn)不用加上單引號或者雙引號;
""
:雙引號狸眼;不會轉(zhuǎn)義字符串里面的特殊字符藤树;特殊字符會作為本身想表示的意思
name: "zhangsan \n lisi":輸出;zhangsan 換行 lisi
''
:單引號拓萌;原樣輸出岁钓,特殊字符最終只是一個普通的字符串?dāng)?shù)據(jù)
name: ‘zhangsan \n lisi’:輸出俱尼;zhangsan \n lisi
對象胞锰、Map:
friends:
lastName: zhangsan
age: 20
行內(nèi)寫法:
friends: {lastName: zhangsan, age: 18}
數(shù)組(List池磁,Set):
用-
標(biāo)志數(shù)組中的一個元素
pets:
- cat
- dog
- pig
行內(nèi)寫法
pets: [cat, dog, pig]
3. 配置屬性列表
配置文件中所有能夠配置的屬性可以查看官方文檔
2.3 配置文件值的注入
1. @ConfigurationProperties 配置文件屬性的綁定
配置文件使用 @ConfigurationProperties 注解與Java類的綁定汇恤。
配置文件:
# application.yml 配置文件囚玫,定義了person對象的值
person:
lastName: 張三 # 使用last-name也可以寸谜,支持松散綁定
age: 18
boss: false
birth: 2002/1/2
maps: {k1: v1, k2: 12}
list:
- lisi
- wangwu
dog:
name: doudou
age: 3
JavaBean:
/**
* 將配置文件中每一個屬性的值朽褪,映射到這個類中
* @ConfigurationProperties 告訴SpringBoot將本類中所有屬性與配置文件中
* 相關(guān)配置進(jìn)行綁定, prefix選擇配置文件中的屬性
*
* @Component 作用是將組件加入 Spring 容器窍霞,使用時 @Autowired 獲取
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
這是一個非常重要的注解罩旋,SpringBoot 自動配置原理也是使用該注解啊央,根據(jù)配置前綴(prefix="spring.dataSource")來讀取相關(guān)配置與配置類 DataSourceProperties 綁定眶诈。
使用@Value注解也可以實(shí)現(xiàn)屬性的映射
/**
* 使用 @Value 注解實(shí)現(xiàn)配置屬性的注入
*/
@Component
public class Person2 {
/**
* <bean class="com.atguigu.springboot.Person">
* <property name="lastName" value="${person.last-name}"></property>
* </bean>
* 在xml中注入bean并配置屬性,Spring注解@Component替代了xml配置注入bean的方式
* 注解@Value用于設(shè)置屬性值
*/
// @Value不支持松散綁定瓜饥,${person.lastName}無法獲取到值逝撬,必須與配置文件key完全一致
@Value("${person.last-name}") // ${} 從配置文件獲取屬性值
// @Email // @Value 不支持JSR303校驗(yàn),該注解無作用
private String lastName;
@Value("#{10+8}") // SpEL表達(dá)式
private Integer age;
IDEA提示功能:
在 pom.xml 中導(dǎo)入配置文件處理器乓土,這樣在配置文件中寫配置就會有提示屬性功能
<!-- 導(dǎo)入配置文件處理器宪潮,配置文件設(shè)置屬性時會有提示 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
2. 松散綁定
在上文例子中,屬性名為lastName
帐我,配置文件中last-name: zhangsan
坎炼,因?yàn)?@ConfigurationProperties 注解支持松散綁定,SpringBoot可以準(zhǔn)確識別并綁定拦键。而@value注解則不支持松散綁定谣光,屬性名必須與配置文件中的 key 完全一致。
對于以下三種寫法芬为,屬性lastName
均可成功匹配綁定:
– person.lastName:
– person.last-name:
– person.last_name:
– PERSON_LAST_NAME:
3. Properties 亂碼問題
SprinBoot 除了 YAML 配置文件萄金,也可以使用 application.properties 作為配置文件,
# server.port=8081
# 配置person的值媚朦,中文會出現(xiàn)亂碼問題
person.last-name=張三
person.age=18
person.birth=2001/2/3
person.boss=false
# 設(shè)置map和數(shù)組的屬性值
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c
# 設(shè)置對象的屬性值
person.dog.name=doudou
person.dog.age=2
YAML 中不會出現(xiàn)中文亂碼問題氧敢,IDEA中 Properties 文件默認(rèn)使用GBK編碼,而IDEA項(xiàng)目使用 UTF-8編碼询张,導(dǎo)致運(yùn)行時亂碼孙乖。
解決辦法: 在IDEA->設(shè)置-> file encoding,將編碼設(shè)置為 UTF-8份氧,勾選 native->ascii唯袄。
修改后,可以在 IDEA 右下角看到文件編碼變?yōu)?UTF-8蜗帜,勾選 native->ascii 的作用是為了方便在 IDEA中查看中文內(nèi)容恋拷。
[圖片上傳失敗...(image-b925ca-1606212213489)]
4. @ConfigurationProperties 與 @Value
- @ConfigurationProperties 與 @Value 兩個注解均是從配置文件中獲取屬性值,并注入給示例相關(guān)屬性厅缺。
- @ConfigurationProperties 一般用于注入屬性值到整個對象蔬顾,更為便捷,支持松散綁定湘捎,支持 JSR303 數(shù)據(jù)校驗(yàn)诀豁。
- @ConfigurationProperties 只是綁定屬性,但并不會將標(biāo)注的類加入到 Spring 容器中窥妇,一般與@Component且叁,@EnableConfigurationProperties,@Bean 配合使用秩伞,將標(biāo)注的類加入到 Spring 容器
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的屬性 | 需要一個個指定屬性值 |
松散綁定 | 支持 | 不支持 |
JSR303數(shù)據(jù)校驗(yàn) | 支持 | 不支持 (2.x版本也支持逞带?) |
SpEL表達(dá)式 | 不支持 | 支持 |
@ConfigurationProperties 組件加入容器的三種方式:
- @EnableConfigurationProperties
// 將配置文件中屬性與該類屬性綁定
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
}
@Configuration(proxyBeanMethods = false)
// 將制定的配置類DataSourceProperties 加入Spring容器欺矫,使其生效
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
}
- @Bean
// 配置制定的 Druid 數(shù)據(jù)源,將方法返回的對象加入Spring容器
@Bean
// 綁定配置文件中的屬性到指定類
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druid() {
return new DruidDataSource();
}
- @Component
@Value 注入 Map 等復(fù)雜類型參考文章展氓,教程中說 @Value 不支持復(fù)雜類型注入是錯誤的穆趴。
- 專門編寫的JavaBean來和配置文件映射,推薦使用 @ConfigurationProperties
- 業(yè)務(wù)邏輯某處使用配置文件中的屬性時遇汞,推薦使用 @Value
5. @PropertySource 與 @ImportResource
@PropertySource: 加載指定的配置文件未妹,默認(rèn)加載application.properties
配置文件。
@Component
// 表示加載person.properties配置文件
@PropertySource({"classpath:person.properties"})
@ConfigurationProperties(prefix = "person")
public class Person {
@ImportResource: 導(dǎo)入 Spring 的配置文件空入,不推薦使用
下面的代碼導(dǎo)入了 Spring 的配置文件 beans.xml络它,配置了JavaBean HelloService,類似于 HelloService 類上添加 @Service 注解歪赢,使自動注入 Spring 容器化戳,可以被 @AutoWired 使用。
@SpringBootApplication
@ImportResource("classpath:beans.xml")
public class SpringBoot02ConfigApplication {
<!-- beans.xml 使用xml方式注入bean -->
<bean id="helloService" class="com.atguigu.springboot.service.HelloService"></bean>
@Component 與 @Bean 類似埋凯,標(biāo)注這個類需要注入到 Spring 容器点楼,與xml中配置<bean>
的作用一致。
6. @Configuration 和 @Bean
小知識: IDEA 中 Spring Beans 與 MVC 的可視化
顯示Spring容器中所有的Bean白对,方便查看Bean是否添加到了容器掠廓,對于Spring的內(nèi)置Bean,還可以直接跳轉(zhuǎn)到文檔查看bean的介紹
[圖片上傳失敗...(image-9f4842-1606212213489)]
顯示所有url映射甩恼,方便查看url與Controller的映射蟀瞧,還可以過濾各種類型的請求
[圖片上傳失敗...(image-549186-1606212213489)]
@Configuration:
@Configuration 底層是 @Component 注解,會被 Spring 掃描添加到容器中
使用全注解的方式映射配置類與 Spring 配置条摸,類似于@ConfigurationProperties悦污,不過后者針對的是配置文件properties。
@Configuration用于定義配置類屈溉,可替換xml配置文件,被注解的類內(nèi)部包含有一個或多個被@Bean注解的方法抬探,這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進(jìn)行掃描子巾,并將這些 Bean 加入到 Spring 容器
@Bean:
- @Bean 用于告訴方法,返回一個Bean對象小压,將其加入Spring容器线梗,產(chǎn)生這個Bean對象的方法Spring只會調(diào)用一次(單例)
- @Bean注解把當(dāng)前方法的返回值作為bean對象存入spring容器中,其name屬性用于指定bean的id(若沒寫該屬性怠益,默認(rèn)值是當(dāng)前的方法名)
- @Bean 是一個方法級別上的注解仪搔,主要用在@Configuration注解的類里,也可以用在@Component注解的類里蜻牢。
SpringBoot 推薦用全注解的方式向容器中添加組件烤咧,替代編寫 Spring XML配置文件
- 標(biāo)注 Spring 配置類 @Configuration
- 使用 @Bean 向容器中添加組件
/**
* 以前在配置文件beans.xml中添加<bean></bean>偏陪,然后使用@ImportResource導(dǎo)入
* @Configuration 標(biāo)注類為一個配置類,就是來替代之前的Spring xml配置文件
*/
@Configuration
public class MyAppConfig {
/**
* 將方法的返回值添加到容器中煮嫌,容器中這個組件Bean默認(rèn)的id就是方法名
* @Bean 給容器中添加組件
*/
@Bean
public HelloService helloService2() {
System.out.println("配置類@Bean給容器中添加組件...");
return new HelloService();
}
}
@Configuration 和 @Bean 的詳細(xì)講解參考Spring注解驅(qū)動教程
2.3 配置文件占位符
1. 屬性配置占位符
可以引用前面定義的屬性笛谦,使用冒號設(shè)置默認(rèn)值
app.name=WeChat
app.description=${app.name:MyApp} is a SpringBoot Application.
2. 隨機(jī)數(shù)
${randowm.uuid}、 ${random.int}昌阿、 ${random.long}
${random.int(10)}饥脑、 ${random.int[1024,65536]}
2.4 Profile 多環(huán)境配置文件切換
生產(chǎn)環(huán)境和開發(fā)環(huán)境一般使用的配置文件并不相同,SpringBoot提供了三種多環(huán)境配置文件切換功能
- 配置文件中指定
# properties設(shè)置啟動的配置文件為 application-dev.properties
spring.profiles.active=dev
# yml設(shè)置啟動的配置文件為 application-dev.yml
spring:
profiles:
active: dev
注意: 是 profiles 不是 profile
- 命令行參數(shù)指定
java -jar MyApp.jar --spring.profiles.active=dev
也可以在IDEA 運(yùn)行->編輯配置->程序參數(shù) 中添加--spring.profiles.active=dev
- JVM 參數(shù)指定
# 生產(chǎn)環(huán)境一般使用 jvm 參數(shù)激活配置文件:
C:\Users\mao> java -jar -Dspring.profiles.active=prod MyApp.jar
2.5 配置文件加載位置
SpringBoot會從以下 4 個位置加載全部配置文件懦冰,不相同的屬性進(jìn)行互補(bǔ)灶轰,優(yōu)先級1級的會覆蓋優(yōu)先級2級的相同的屬性配置,知曉即可刷钢,不常用
demo/ # 項(xiàng)目根路徑
config/
application.properties # 優(yōu)先級1
application.properties # 優(yōu)先級2
src/
java/
resource/ # classpath:下面的文件打包后會在 classes下
config/
application.properties # 優(yōu)先級2
application.properties # 優(yōu)先級4(常用)
在項(xiàng)目打包完成后笋颤,需要修改配置時可以使用命令行參數(shù)spring.config.location
指定配置文件,優(yōu)先級最高
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
2.6 配置文件加載順序(重點(diǎn))
優(yōu)先級由高到低闯捎,高優(yōu)先級會覆蓋低優(yōu)先級配置:
- 命令行參數(shù)
- Java系統(tǒng)屬性(System.getProperties())
- jar包外部的application-{profile}.properties/yml(帶spring.profile)配置文件
- jar包內(nèi)部的application-{profile}.properties/yml(帶spring.profile)配置文件
- jar包外部的application.properties/yml(不帶spring.profile)配置文件
- jar包內(nèi)部的application.properties/yml(不帶spring.profile)配置文件
- @Configuration注解類上的@PropertySource指定的配置文件(參考#2.3.5 中Person類指定的配置文件)
- 通過SpringApplication.setDefaultProperties指定的默認(rèn)屬性
命令行參數(shù)適用生產(chǎn)環(huán)境修改某個配置時使用椰弊,jar包外面的配置文件,適合生產(chǎn)環(huán)境批量修改多個配置時使用
2.7 自動配置原理(重難點(diǎn))
// 補(bǔ)充:自動配置原理其實(shí)并沒有搞懂瓤鼻,后面進(jìn)行補(bǔ)充
- SpringBoot 啟動時加載主程序類秉版,@SpringBootApplication 注解包含了 @EnableAutoConfiguration
- @EnableAutoConfiguration 表示開啟自動配置功能
- @EnableAutoConfiguration 作用是將 META-INF/spring.factories 里面配置的所有XXXAutoConfiguration的組件加入到了容器中
- 源碼參考 AutoConfigurationImportSelector#selectImports,里面獲取了所有自動配置類茬祷,如 DataSourceAutoConfiguration清焕,WebMvcAutoConfiguration
- xxxAutoConfiguration類都是容器中的一個組件,都加入到容器中祭犯;用他們來做自動配置秸妥,如
HttpEncodingAutoConfiguration
來做Http編碼自動配置
@Configuration //表示這是一個配置類,以前編寫的配置文件一樣沃粗,也可以給容器中添加組件
//讓指定的 HttpEncodingProperties 配置類生效粥惧;將配置文件中對應(yīng)的值和HttpEncodingProperties綁定起來;并把 HttpEncodingProperties加入到ioc容器中
@EnableConfigurationProperties(HttpEncodingProperties.class)
//Spring底層@Conditional注解(Spring注解版)最盅,根據(jù)不同的條件突雪,如果 滿足指定的條件,整個配置類里面的配置就會生效涡贱; 判斷當(dāng)前應(yīng)用是否是web應(yīng)用咏删,如果是,當(dāng)前配置類生效
@ConditionalOnWebApplication
//判斷當(dāng)前項(xiàng)目有沒有這個類 CharacterEncodingFilter问词;SpringMVC中進(jìn)行亂碼解決的過濾器督函;
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //判斷配置文件中是否存在某個配置 spring.http.encoding.enabled;如果不存在,判斷也是成立的 //即使我們配置文件中不配置pring.http.encoding.enabled=true辰狡,也是默認(rèn)生效的锋叨;
public class HttpEncodingAutoConfiguration {
//他已經(jīng)和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;
- 所有在配置文件中能配置的屬性都是在 xxxxProperties 類中封裝著,配置文件能配置什么就可以參照某個功能對應(yīng)的這個屬性類搓译。如
HttpEncodingProperties.class
//從配置文件中獲取指定的值和bean的屬 性進(jìn)行綁定 @ConfigurationProperties(prefix = "spring.http.encoding") public class HttpEncodingProperties { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");
·
擴(kuò)展:
向Spring容器添加組件的三種方式:
- @Componet悲柱,@Controller,@Service些己,@Repository豌鸡,@Configuration:局限于自己寫的組件
- @Bean:注解用于告訴方法,返回一個Bean對象段标,將其加入Spring容器涯冠。產(chǎn)生這個Bean對象的方法Spring只會調(diào)用一次(單例)。Bean導(dǎo)入第三方包的組件逼庞,是一個方法級別上的注解蛇更,主要用在@Configuration注解的類里,也可以用在@Component注解的類里赛糟。@Bean注解把當(dāng)前方法的返回值作為bean對象存入spring容器中派任,其name屬性用于指定bean的id(若沒寫該屬性,默認(rèn)值是當(dāng)前的方法名)
- @Import:導(dǎo)入組件
2.8 @Conditional 自動配置報(bào)告
// 補(bǔ)充:沒搞懂璧南,配合SPring注解教程學(xué)習(xí)
第 3 章 SpringBoot 日志
3.1 常見日志框架
小張掌逛;開發(fā)一個大型系統(tǒng);
- 剛開始使用System.out.println("")司倚;將關(guān)鍵數(shù)據(jù)打印在控制臺豆混;
- 為了記錄記錄系統(tǒng)的一些運(yùn)行時信息,開發(fā)了一個日志框架 zhanglogging-1.0.jar动知;
- 迭代更新日志框架皿伺,異步模式,按日期自動歸檔等 zhanglogging-2.0.jar
- 為了替換日志框架盒粮,重新修改之前相關(guān)的API鸵鸥,特別麻煩
- 參考 JDBC--數(shù)據(jù)庫驅(qū)動,門面設(shè)計(jì)模式丹皱,統(tǒng)一的接口層妒穴,不同數(shù)據(jù)庫的代碼都是一樣的,需要數(shù)據(jù)庫去實(shí)現(xiàn)驅(qū)動接口
- 實(shí)現(xiàn)一個統(tǒng)一的接口層:日志門面(日志的一個抽象層)logging-abstract.jar
- 給項(xiàng)目中導(dǎo)入具體的日志實(shí)現(xiàn)就行了种呐,日志框架升級宰翅、替換也不需要修改我們的代碼
日志門面(抽象層) | 日志實(shí)現(xiàn) |
---|---|
SLF4j弃甥, |
Logback爽室,Log4j,Log4j2 |
Spring 日志框架是 jakarta-commons-Logging,SpringBoot 默認(rèn)日志框架是 SLF4j + Logback (推薦使用)
3.2 SLF4j 使用
參考SLF4j 官網(wǎng)手冊阔墩,記錄日志只需要使用 SLF4j 的 API嘿架,不應(yīng)該直接調(diào)用日志框架 Logback 的實(shí)現(xiàn)類。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
3.3 SpringBoot 日志關(guān)系(了解)
SpringBoot-Starter-Web 依賴 spring-boot-starter啸箫,后者又依賴 spring-boot-starter-logging耸彪,SpringBoot 使用它來做日志記錄。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
SpringBoot 依賴 Spring忘苛,而 Spring 使用的是 jakarta-commons-Logging 日志框架蝉娜,需要集成的Hibernate 使用 jboss-logging日志框架,SLF4j 把這些日志框架也都替換為了 SLF4j 框架扎唾。
[圖片上傳失敗...(image-ef1761-1606212213489)]
總結(jié):
- SpringBoot 底層也是使用 slf4j+logback 的方式進(jìn)行日志記錄
- SpringBoot 也把其他的日志都替換成了 slf4j 中間替換包
- slf4j 中間替換包本質(zhì)是將 jakarta-commons-Logging 等日志框架使用相同類名重寫了一次
- 如果要引入其他日志框架召川,需要將中間替換包移除
slf4j 中間替換包本質(zhì)是將 JCL(jakarta-commons-Logging) Log4j 等日志框架使用相同類名重寫了一次,下圖是slf4j的替換包胸遇,與被替換框架的包名荧呐、類名完全一致。
3.4 日志使用
// 日志記錄器纸镊,注意是slf4j 的 LoggerFactory
Logger logger = LoggerFactory.getLogger(getClass());
/**
* 使用slf4j倍阐,
*/
@Test
void contextLoads() {
// 日志級別由低到高
logger.trace("這是trace日志...");
logger.debug("這是debug日志...");
// 默認(rèn)使用info級別日志輸出
logger.info("這是info日志...");
logger.warn("這是warn日志...");
logger.error("這是error日志...");
}
1. 常見日志配置屬性
在 SpringBoot 配置文件 application.properties
中修改日志配置
# 生產(chǎn)環(huán)境下設(shè)置項(xiàng)目所有日志級別為WARN
# logging.level.root=WARN
# 設(shè)置下面包 com.atguigu 的日志級別為trace
logging.level.com.atguigu=TRACE
logging.file.path=/mao/log
# 指定日志輸出文件,默認(rèn)在當(dāng)前項(xiàng)目根目錄逗威,與1.x的屬性不同
logging.file.name=springboot.log
# 設(shè)置在控制臺輸出日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 設(shè)置在日志文件輸出日志的格式
logging.pattern.file=
# 設(shè)置控制臺日志顏色
spring.output.ansi.enabled=ALWAYS
# 設(shè)置啟動時打印所有的請求路徑映射
logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=trace
測試環(huán)境可以使用命令行輸出 DEBUG 級別的日志記錄
$ java -jar myapp.jar --debug
2. 指定日志框架配置文件
在 SpringBoot 配置文件 application.properties
中修改日志配置峰搪,如果要修改的屬性過多,推薦加入日志框架自己的配置文件庵楷,會自動生效
Logging System | 配置文件名稱 |
---|---|
Logback | logback-spring.xml罢艾,logback.xml |
Log4J2 | log4j2-spring.xml,log4j2.xml |
logback-spring.xml
這種名稱可以使用 SpringBoot profile 高級功能尽纽,即在不同環(huán)境下使用不同的日志配置咐蚯。使用spring.profiles.active=dev
屬性控制。
<springProfile name="dev">
<!-- 可以指定某段配置在開發(fā)環(huán)境下生效 -->
<!-- spring.profiles.active=dev 配置該屬性設(shè)置環(huán)境為開發(fā)環(huán)境 -->
</springProfile>
logback-spring.xml 的配置在項(xiàng)目 spring-boot-03-logging 下弄贿,有詳細(xì)注釋春锋,使用時可進(jìn)行參考。
3.5 切換日志框架
切換為 slf4j + log4j:
排除 SpringBoot 默認(rèn)依賴的日志框架差凹,移除 logback 依賴期奔,移除 slf4j-log4j 中間包,然后引入 log4j 依賴危尿。由于 log4j 框架性能一般呐萌,不推薦使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
切換為 slf4j + log4j2:
SpringBoot 提供 log4j2 starter 啟動器,包含了 log4j2 及 slf4j-log4j2 中間包肺孤,移除之前自帶的 logging starter依賴,添加 log4j2 啟動器依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring‐boot‐starter‐logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
// 補(bǔ)充:SpringBoot 2.x 版本日志框架有了變化赠堵,沒有這么麻煩了小渊?
第 4 章 SpringBoot 與 WEB 開發(fā)
4.1 簡介
SpringBoot 開發(fā)三步走:
- 創(chuàng)建SpringBoot 應(yīng)用,選擇需要使用的模塊茫叭,如 web酬屉,Mybatis等
- SpringBoot 已經(jīng)默認(rèn)將這些場景配置好了,只需要配置文件中指定少量配置就可以運(yùn)行起來
- 編寫業(yè)務(wù)代碼
自動配置原理:
自動配置相關(guān)的類都在spring-boot-autoconfiguration-2.3.1.jar
中
-
DataSourceAutoConfiguration
:數(shù)據(jù)源自動配置類揍愁,該類使用注解 @EnableConfigurationProperties 讓指定的數(shù)據(jù)源配置映射類 DataSourceProperties 生效 -
DataSourceProperties
:數(shù)據(jù)源配置映射類呐萨,所有可以配置的屬性都在該類中,該類使用注解 @ConfigurationProperties 指定映射的配置屬性的前綴 prefix="spring.datasource"
小知識: 分析類的依賴
IDEA 中右鍵相關(guān)類莽囤,選擇分析->分析依賴->整個項(xiàng)目垛吗,可以看到當(dāng)前類的依賴。如下圖所示烁登,項(xiàng)目主程序依賴了String怯屉、SpringBootApplication 等類。在大型項(xiàng)目中不熟悉項(xiàng)目結(jié)構(gòu)可以使用這種方式查看饵沧。
[圖片上傳失敗...(image-12175f-1606212213489)]
4.2 SpringBoot 靜態(tài)資源映射規(guī)則
1. Webjars
webjars 是以jar包的方式引入靜態(tài)資源
對于日常的web開發(fā)而言锨络,像css、js狼牺、images羡儿、font等靜態(tài)資源文件管理是非常的混亂的、比如jQuery是钥、
Bootstrap掠归、Vue.js等,可能每個框架使用的版本都不一樣悄泥、一不注意就會出現(xiàn)版本沖突或者重復(fù)添加的問題虏冻。所以誕生了 WebJars 技術(shù)。
原本我們在進(jìn)行web開發(fā)時弹囚,一般上都是講靜態(tài)資源文件放置在webapp目錄下厨相,在SpringBoot里面,一般是將資源文件放置在src/main/resources/static目錄下鸥鹉。而在Servlet3中蛮穿,允許我們直接訪問WEB-INF/lib下的jar包中的/META-INF/resources目錄資源,即WEB-INF/lib/{*.jar}/META-INF/resources下的資源可以直接訪問毁渗。
WebJars 正是利用了此功能践磅,將所有前端的靜態(tài)文件打包成一個jar包.
對于用戶而言,和普通的jar引入是一樣的灸异,還能很好的對前端靜態(tài)資源進(jìn)行管理府适。WebJars是將這些通用的Web前端資源打包成Java的Jar包幻碱,然后借助Maven工具對其管理,保證這些Web資源版本唯一性细溅,依賴配置參考 https://www.webjars.org/
- 所有的/webjars/** 下的靜態(tài)資源,都去 classpath:/META-INF/resources/webjars/ 下尋找資源
<!-- 以webjars的方式引入 jQuery 依賴 -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
引入 jQuery webjar 的依賴儡嘶,結(jié)構(gòu)如下圖所示喇聊,jquery.js 確實(shí)在 classpath:/META-INF/resources/webjars/ 目錄下:
- 訪問靜態(tài)資源jquery.js http://localhost:8080/webjars/jquery/3.3.1/jquery.js
靜態(tài)資源的配置類為 ResourceProperties,源碼如下:
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
//可以設(shè)置和靜態(tài)資源有關(guān)的參數(shù)箭券,緩存時間等
WebMvcAutoConfiguration:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
// 未注冊的資源熟菲,都去靜態(tài)資源目錄尋找
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
// 添加靜態(tài)資源目錄
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
-
靜態(tài)資源路徑撩荣,classpath 指項(xiàng)目/src/main/resource,與java類的根路徑同級窜骄,使用java/resource區(qū)分資源,本身都是在classpath下摆屯,打包后剛好在classpath下即\BOOT-INF\classes
- classpath:/META‐INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /:當(dāng)前項(xiàng)目的根路徑
歡迎頁映射邻遏,訪問 localhost:8080,會去靜態(tài)資源文件夾下尋找index頁面
WebMvcAutoConfiguration:
//配置歡迎頁映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ResourceProperties resourceProperties) {
return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
- 網(wǎng)站icon映射虐骑,會去靜態(tài)資源文件夾下尋找 favicon.ico
小知識
IDEA 搜索 文件: 雙擊 Shift 快捷鍵准验,可以搜索項(xiàng)目和庫中的 類、符號(屬性方法)廷没、模板(xml等文件)
IDEA 搜索 文件內(nèi)容: Ctrl+Shift+F 快捷鍵糊饱,可以搜索項(xiàng)目中的內(nèi)容,支持限制文件類型(.xml颠黎,.java)另锋,還可以限制搜索 注釋、字符串等狭归。
4.3 模板引擎
模板引擎作用是將模板與動態(tài)數(shù)據(jù)結(jié)合生成html頁面
[圖片上傳失敗...(image-76c3ac-1606212213489)]
常見的模板引擎:JSP夭坪,Thymeleaf,F(xiàn)reemarker
SpringBoot 推薦使用 Thymeleaf过椎,語法簡單台舱,功能強(qiáng)大。
1. 引入Thymeleaf
Thymeleaf 讀作 [taim li:f]
<!-- 引入thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2. Thymeleaf 的使用
根據(jù) Thymeleaf 默認(rèn)配置可知竞惋,只要講HTML頁面放在classpath:/templates/
下(就是src/main/resources/templates/),Thymeleaf 就能自動渲染灰嫉。
Thymeleaf只渲染 /templates 下的頁面HTML頁面 拆宛,不能放在static或resource下面。
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
- 在 src/main/resources/templates/ 下創(chuàng)建 success.html 文件
- 向 success.html 中導(dǎo)入 Thymeleaf 的名稱空間讼撒,代碼提示功能還需要使用IDEA-U版打開 Thymeleaf 插件
- 使用 Thymeleaf 語法編寫html文件浑厚,<div th:text="${key1}"></div>
// 攜帶map數(shù)據(jù)股耽,跳轉(zhuǎn)到success.html頁面,
@RequestMapping("/hello2")
public String hello2(Map<String, String> map) {
map.put("key1", "hello thymeleaf...");
// 跳轉(zhuǎn)到 classpath:/templates/success.html
return "success";
}
success.html:
<!DOCTYPE html>
<!-- 引入Thymeleaf名稱空間钳幅,配合IDEA收費(fèi)版才有代碼提示功能 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>訪問success成功...</h2>
<!-- th:text設(shè)置div內(nèi)的文本內(nèi)容 -->
<!-- 如果不經(jīng)過模板引擎解析物蝙,則顯示"你好",否則顯示后端傳遞的hello變量-->
<div th:text="${key1}">你好</div>
</body>
</html>
小知識 :IDEA-U 激活
- http://idea.medeming.com/jet/
- http://lookdiv.com/
- http://www.reibang.com/p/d1b1ff342951?tdsourcetag=s_pctim_aiomsg
- 淘寶
4. Thymeleaf 語法
標(biāo)簽:
-
th:text
改變當(dāng)前元素內(nèi)的文本內(nèi)容th: 可以搭配任意的 html 屬性敢艰,來覆蓋原生屬性的值诬乞,如
th:id
th:class
<!-- 后臺傳來的數(shù)據(jù)會覆蓋掉html中的標(biāo)簽數(shù)據(jù),id钠导,class等 --> <div id="div01" class="div02" th:id="${myId}" th:class="${myClass}" th:text="${hello}">你好</div>
th:each
遍歷標(biāo)簽th:if
條件標(biāo)簽更多th標(biāo)簽參考官方文檔
[圖片上傳失敗...(image-da2260-1606212213489)]
表達(dá)式:
- ${} 變量震嫉,后臺傳過來的變量
-
{} 國際化
- @{} 項(xiàng)目根路徑,引入css牡属,js等
- ~{} 頁面公共片段(導(dǎo)航欄)引入
-
[[${}]]
獲取變量行內(nèi)寫法票堵,等價于標(biāo)簽內(nèi)th:text="${}"
Simple expressions:
Variable Expressions: ${...} # 獲取變量值,對象屬性逮栅,數(shù)組元素悴势,內(nèi)置對象
Selection Variable Expressions: *{...}
Message Expressions: #{...} # 國際化內(nèi)容
Link URL Expressions: @{...} # 定義URL,表示項(xiàng)目根路徑措伐,引用webjar中的css資源
Fragment Expressions: ~{...} # 頁面公共片段引入
Literals
Text literals: 'one text', 'Another one!',…
Number literals: 0, 34, 3.0, 12.3,…
Boolean literals: true, false
Null literal: null
Literal tokens: one, sometext, main,…
Arithmetic operations: # 數(shù)學(xué)運(yùn)算
Binary operators: +, -, *, /, %
Minus sign (unary operator): -
Boolean operations: # 布爾運(yùn)算
Binary operators: and, or
Boolean negation (unary operator): !, not
Comparisons and equality: # 比較運(yùn)算
Comparators: >, <, >=, <= (gt, lt, ge, le)
Equality operators: ==, != (eq, ne)
Conditional operators: # 條件運(yùn)算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
Special tokens:
語法比較亂瞳浦,和Freemarker一樣,用的時候多查多模仿多總結(jié)即可废士,不要死記硬背叫潦。
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax
遍歷數(shù)組的兩種方式:
<!-- 遍歷數(shù)組方式1,后臺傳遞過來的數(shù)組 users -->
<h4 th:each="user:${users}" th:text="${user}"> </h4>
<!-- 遍歷數(shù)組方式2官硝,行內(nèi)寫法矗蕊,與上面等價 -->
<h4>
<span th:each="user:${users}">[[${user}]]</span>
</h4>
引入webjars的靜態(tài)資源
<link th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
4.4 SpringMVC 自動配置
SpringBoot 自動配置好了 SpringMVC,包含以下默認(rèn)配置:
配置了ViewResolver視圖解析器氢架,根據(jù)方法的返回值得到視圖對象傻咖,渲染頁面
ContentNegotiatingViewResolver
BeanNameViewResolver
靜態(tài)資源路徑、webjars
靜態(tài)首頁訪問 index.html
網(wǎng)站圖標(biāo)訪問 Favicon.ico
-
自動注冊了 Converter岖研,GenericConverter卿操,F(xiàn)ormatter bean
- Converter:轉(zhuǎn)換器,類型轉(zhuǎn)換
- Formatter:格式化器孙援,時間格式轉(zhuǎn)換
HttpMessageConverters SpringMVC用來轉(zhuǎn)換http請求和響應(yīng)害淤,比如將user對象轉(zhuǎn)為json返回給瀏覽器
// 補(bǔ)充: p31 p32源碼這里不太懂建議復(fù)習(xí)SpringMVC,9小時
4.5 修改 SpringBoot 默認(rèn)配置
- SpringBoot在自動配置很多組件的時候拓售,先看容器中有沒有用戶自己配置的(@Bean窥摄、@Component)如 果有就用用戶配置的,如果沒有础淤,才自動配置崭放;如果有些組件可以有多個(ViewResolver)將用戶配置的和自己默 認(rèn)的組合起來哨苛;
- 在SpringBoot中會有非常多的xxxConfigurer幫助我們進(jìn)行擴(kuò)展配置
- 在SpringBoot中會有很多的xxxCustomizer幫助我們進(jìn)行定制配置
4.6 Restful crud 項(xiàng)目
1. 國際化
創(chuàng)建和編寫國際化配置文件
[圖片上傳失敗...(image-ea5caa-1606212213489)]SpringBoot 自動配置好了 ResourceBundleMessageSource 管理國際化資源文件的組件,默認(rèn)國際化文件路徑為
classpath:message.properties
币砂,需要修改如下
# application.properties 設(shè)置國際化資源文件目錄,默認(rèn)為classpath:messages
spring.messages.basename=i18n.login
通過源碼可知建峭,國際化資源文件默認(rèn)路徑為classpath:message.properties
ResourceBundleMessageSource:
private String basename = "messages";
public String getBasename() {
return this.basename;
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
// 設(shè)置國際化資源文件的基礎(chǔ)名為message.properties,放在類路徑下
// 這里去除了國際化文件的語言國家后綴
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
- 在頁面使用
#{}
獲取國際化內(nèi)容
<label class="sr-only" th:text="#{login.username}">Username</label>
-
修改瀏覽器語言决摧,訪問頁面亿蒸,查看國際化效果
修改瀏覽器語言為
English(United States)
,此時的請求體中Accept-Language: en-US,zh-CN;
頁面顯示語言為英文蜜徽,相關(guān)資源在login.en_US.properties
。注意:如果將瀏覽器語言修改為
English
票摇,那么國際化將不生效拘鞋,此時的請求體中Accept-Language: en,zh-CN;
,需要配置文件login_en.properties
才能生效矢门。
1. 國際化原理
國際化Locale(區(qū)域信息對象) LocaleResolver(獲取區(qū)域信息對象)
WebMvcAutoConfiguration:
@Bean
@ConditionalOnMissingBean // 如果容器中不存在Id相同的bean盆色,才使用該bean,即用戶自定義bean可以替代
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
// 優(yōu)先使用默認(rèn)的locale信息
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
// 根據(jù)瀏覽器請求體中的AcceptHeader設(shè)置LocalResolver信息
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
2. 實(shí)現(xiàn)語言切換功能
- 修改前面頁面祟剔,添加語言切換按鈕隔躲,發(fā)送請求攜帶參數(shù)
l='zh_CN'
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
- 自定義區(qū)域解析器MyLocaleResolver,根據(jù)請求參數(shù)修改語言
/**
* 實(shí)現(xiàn)網(wǎng)站語言切換功能
* 自定義LocaleResolver實(shí)現(xiàn)LocaleResolver
*/
public class MyLocaleResolver implements LocaleResolver {
// 解析區(qū)域信息物延,優(yōu)先返回 用戶選擇的語言>瀏覽器語言>服務(wù)器默認(rèn)語言
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 獲取切換語言請求中的參數(shù)
String l = request.getParameter("l");
if (!StringUtils.isEmpty(l)) {
String language = l.split("_")[0];
String country = l.split("_")[1];
// 根據(jù)語言與國家信息en_US創(chuàng)建 locale
Locale locale = new Locale(language, country);
return locale;
}
// 獲取瀏覽器的區(qū)域信息 accept-language
String acceptLanguage = request.getHeader("Accept-Language");
if (!StringUtils.isEmpty(acceptLanguage)) {
Locale requestLocale = request.getLocale();
return requestLocale;
}
// 返回項(xiàng)目系統(tǒng)區(qū)域信息
return Locale.getDefault();
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
- 將區(qū)域解析器MyLocaleResolver加入到容器宣旱,使其生效
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
// 向容器中加入我們自定義的bean MyLocaleResolver,bean的id為方法名叛薯,
// 用于替換默認(rèn)的localeResolver浑吟,當(dāng)然也可以在@中設(shè)置id
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
2. 登錄
小知識: Thymeleaf 頁面修改實(shí)時生效,不用重啟項(xiàng)目
開發(fā)期間耗溜,模板引擎頁面修改以后组力,要實(shí)時生效
- 禁用模板引擎緩存
spring.thymeleaf.cache=false
- 頁面修改完成后 ctrl+f9,重新編譯
-
登錄表單
<form class="form-signin" th:action="@{/user/login}" method="post"> <input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required=""> </form>
-
處理登錄請求抖拴,登錄成功后燎字,一定要重定向到主頁,如果是轉(zhuǎn)發(fā)阿宅,那么刷新頁面就會有表單重復(fù)提交問題
@RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam String password, Map<String, Object> map, HttpSession session) { if ("admin".equals(username) && "admin".equals(password)) { // 在session中保存一個屬性候衍,用于訪問時識別是否登錄,攔截器中會使用 session.setAttribute("loginUser", username); // 防止表單重復(fù)提交洒放,重定向到主頁 return "redirect:/dashboard"; } // 為啥能將返回map脱柱? SpringMvc入?yún)⒅械膍ap,方法返回時拉馋,會將Map數(shù)據(jù)添加到模型中 map.put("msg", "用戶名或密碼錯誤"); return "index"; }
-
顯示登錄失敗信息
<p th:if="${not #strings.isEmpty(msg)}" style="color: red" th:text="${msg}"></p>
-
攔截器榨为,
http://localhost:8080/crud/dashboard
惨好,需要登錄才能訪問,所以設(shè)置攔截器檢查用戶是否登錄繼承HandlerInterceptor實(shí)現(xiàn)登錄攔截器随闺,如果請求 session 中沒有 loginUser日川,則在請求域設(shè)置權(quán)限信息,再轉(zhuǎn)發(fā)到首頁矩乐。
Session對應(yīng)的類為javax.servlet.http.HttpSession類龄句。每個來訪者對應(yīng)一個Session對象,所有該客戶的狀態(tài)信息都保存在這個Session對象里散罕。Session對象是在客戶端第一次請求服務(wù)器的時候創(chuàng)建的分歇,保存在服務(wù)端。Servlet里通過request.getSession()方法欧漱,根據(jù)請求中攜帶的 JsessionID 在服務(wù)端查詢對應(yīng)的客戶Session职抡,然后獲得該客戶的所有 Session 屬性,如果能查到误甚,說明服務(wù)端保存了該會話缚甩,所以不需要登錄,如果查不到窑邦,則說明服務(wù)端不認(rèn)識該會話擅威,需要登錄。
攔截器在 DispatchServlet.doDisatch 后生效冈钦,在具體請求的 Controller 前生效
攔截器是通過 AOP 實(shí)現(xiàn)
例如:
```java
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 根據(jù)request中攜帶的jsessionId郊丛,查詢對應(yīng)的Session,找到對應(yīng)屬性的值瞧筛,如果服務(wù)端保存了該會話宾袜,則不需要重新登錄
Object user = request.getSession().getAttribute("loginUser");
//判斷用戶身份在session中是否存在,其他屬性都可以驾窟,不一定要是loginUser庆猫,也可以在登錄時保存登錄時間到session
if(user == null) {
request.setAttribute("msg", "沒有權(quán)限,請先登錄");
// 用戶未登錄绅络,則轉(zhuǎn)發(fā)到首頁月培,只有轉(zhuǎn)發(fā)才能攜帶請求信息
request.getRequestDispatcher("/").forward(request, response);
return false;
}
return true;
}
```
- 注冊登錄攔截器,設(shè)置攔截的url
```java
@Configuration
public class MyMvcConfig extends WebMvcConfigurer {
// 添加url與視圖的映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
/*
* 瀏覽器發(fā)送 /atguigu 請求來到 success
* 因?yàn)閟uccess.html保存在/templates中恩急,只有模板引擎能夠訪問杉畜,所以需要映射url與頁面success
* 為什么 index.html沒有在這里綁定url也能訪問? 因?yàn)樵?HelloController中綁定了
*/
registry.addViewController("/atguigu").setViewName("success");
// 映射url與視圖衷恭,前者是url此叠,后者是頁面,與LoginController.dashboard()作用一致
registry.addViewController("/dashboard").setViewName("dashboard");
}
// 注冊攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Springboot已經(jīng)做好了靜態(tài)資源映射随珠,所以我們不需要設(shè)置靜態(tài)資源
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/", "/index.html", "/user/login", "/asserts/**", "/webjars/**");
}
/**
* 向容器中加入我們自定義的bean MyLocaleResolver灭袁,bean的id為方法名猬错,
* 用于替換默認(rèn)的localeResolver,當(dāng)然也可以在@中設(shè)置id
*/
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
}
```
3. Restful CRUD
普通請求與 RestfulCRUD 的區(qū)別如下:
功能 | 普通CRUD(uri區(qū)分操作) | RestfulCRUD |
---|---|---|
查詢 | getEmp | emp-GET |
添加 | addEmp?xxx | emp-POST |
修改 | updateEmp?id=123&xxx=xx | emp/{id}-PUT |
刪除 | deleteEmp?id=123 | emp/{id}-DELETE |
下面來實(shí)現(xiàn)員工的 RestfulCRUD:
功能 | 請求URI | 請求方式 |
---|---|---|
查詢所有員工 | emps | GET |
查詢某個員工(訪問修改頁面) | emp/{id} | GET |
訪問添加員工頁面 | emp | GET |
添加員工 | emp | POST |
修改員工 | emp | PUT |
刪除員工 | emp/{id} | DELETE |
4. 頁面公共元素抽取
- 抽取頁面公共元素茸歧,如導(dǎo)航欄等
1. 使用 ~{templatename::fragmentname}:模板名::片段名
抽取公共片段:
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
引入公共片段:footer是公共片段的文件名
<div th:insert="~{footer :: copy}">
</div>
2. 使用 ~{templatename::#selector}:模板名::選擇器
<div id="copy1">
© 2011 The Good Thymes Virtual Grocery
</div>
引入公共片段
<div th:insert="~{footer :: #copy1}">
</div>
3. 默認(rèn)效果: th:insert 是將公共片段放在下面的 div 標(biāo)簽中
<div th:insert="~{footer :: #copy1}">
三種引入公共片段的th屬性:
- th:insert:將公共片段整個插入到聲明引入的元素中
- th:replace:將聲明引入的元素替換為公共片段 (推薦使用)
- th:include:將被引入的片段的內(nèi)容包含進(jìn)這個標(biāo)簽中
詳細(xì)參考 Thymeleaf 8.2 Template Layout
5. 顯示員工列表
顯示員工列表:GET請求倦炒,url 為 /emps,查詢到的員工集合保存到model中返回前臺软瞎。
@GetMapping("/emps")
public String emps(Model model) {
Collection<Employee> employees = employeeDao.getAll();
// 添加數(shù)據(jù)到請求域中
model.addAttribute("emps", employees);
// 跳轉(zhuǎn)到Thymeleaf渲染的 classpath:/templates/emp/list.html
return "/emp/list";
}
使用 th:each 遍歷員工集合 emps逢唤,使用三元表達(dá)式顯示性別,使用內(nèi)置 date 對象格式化日期
<tbody>
<tr th:each="emp : ${emps}">
<td th:text="${emp.id}"></td>
<td th:text="${emp.lastName}"></td>
<td th:text="${emp.gender}==1 ? '男' : '女' "></td>
<td th:text="${emp.department.departmentName}"></td>
<td th:text="${emp.email}"></td>
<!-- 修改日期格式 -->
<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd')}"></td>
<td >
<button class="btn btn-sm btn-primary">編輯</button>
<button class="btn btn-sm btn-danger" >刪除</button>
</td>
</tr>
</tbody>
6. 跳轉(zhuǎn)到員工頁面
員工列表顯示頁面涤浇,加入【添加員工】按鈕鳖藕,請求 /emp
<a class="btn btn-sm btn-success" th:href="@{/emp}">添加員工</a>
返回添加員工頁面:響應(yīng) GET請求 /emp,添加員工頁面需要顯示部門名稱只锭,所以將部門集合返回著恩,跳轉(zhuǎn)到 /emp/add.html
@GetMapping("/emp")
public String toAddPage(Model model) {
// 查詢部門,返回到添加頁面
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "/emp/add";
}
添加員工頁面:add.html纹烹,遍歷顯示部門名稱页滚,設(shè)置部門信息為id
<form th:action="@{/emp}" method="post">
<div class="form-group">
<label>LastName</label>
<input name="lastName" type="text" class="form-control" placeholder="zhangsan" >
</div>
<div class="form-group">
<label>department</label>
<!-- 遍歷返回的部門集合召边,顯示部門名稱 -->
<!--提交的是部門的id铺呵,發(fā)送的數(shù)據(jù)名稱name是 Employee.department.id,值value是dept.id 級聯(lián)屬性-->
<select class="form-control" name="department.id">
<option th:each="dept:${departments}"
th:text="${dept.departmentName}"
th:value="${dept.id}">
</option>
</select>
</div>
</form>
7. 添加員工
添加員工:POST請求/emp隧熙,保存員工信息后重定向請求員工列表/emps片挂,返回員工列表
- SpringMVC自動將請求參數(shù)與入?yún)ο蟮膶傩赃M(jìn)行一一綁定,Employee屬性與請求參數(shù)名稱一致
- 其中部門信息傳過來的是
department.id=123
贞盯,會自動與javabean Employee 的屬性 department 對象的 id 綁定起來音念。級聯(lián)屬性的綁定 - 添加操作完成后,需要返回員工列表list.html頁面躏敢,但是不能返回闷愤,因?yàn)樘砑诱埱鬀]有查詢所有員工數(shù)據(jù)并返回,所以我們選擇重定向到顯示員工請求 /emps件余,該請求會查詢所有員工并返回到list.html頁面讥脐,參考第5小節(jié) 顯示員工列表
// 添加員工
// SpringMVC自動將請求參數(shù)與入?yún)ο蟮膶傩赃M(jìn)行一一綁定
@PostMapping("/emp")
public String addEmp(Employee employee) {
System.out.println("保存的員工信息" + employee);
employeeDao.save(employee);
// 添加完成后不應(yīng)該返回/emp/list.html,因?yàn)檫@個請求不會攜帶list頁面需要的員工數(shù)據(jù)
// 應(yīng)該轉(zhuǎn)發(fā)或重定向到/emps請求顯示全部員工數(shù)據(jù)
// redirect:表示重定向到一個新地址
// forward:表示轉(zhuǎn)發(fā)到一個新地址
return "redirect:/emps";
}
8. 重定向與轉(zhuǎn)發(fā)
重定向:發(fā)送一個新的請求啼器,并且不攜帶本次請求的數(shù)據(jù)
轉(zhuǎn)發(fā):
p40 重定向與轉(zhuǎn)發(fā)源碼講解
ThymeleafViewResolver:
protected View createView(String viewName, Locale locale) throws Exception {
if (!this.alwaysProcessRedirectAndForward && !this.canHandle(viewName, locale)) {
vrlogger.trace("[THYMELEAF] View \"{}\" cannot be handled by ThymeleafViewResolver. Passing on to the next resolver in the chain.", viewName);
return null;
} else {
String forwardUrl;
// 視圖名稱以redirect:開頭旬渠,則進(jìn)行重定向
if (viewName.startsWith("redirect:")) {
vrlogger.trace("[THYMELEAF] View \"{}\" is a redirect, and will not be handled directly by ThymeleafViewResolver.", viewName);
forwardUrl = viewName.substring("redirect:".length(), viewName.length());
// 最終在renderMergedOutputModel方法 中調(diào)用servlet原生重定向response.sendRedirect(encodedURL);
RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());
return (View)this.getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
} else if (viewName.startsWith("forward:")) {
// 視圖名稱以forward:,則進(jìn)行轉(zhuǎn)發(fā)
vrlogger.trace("[THYMELEAF] View \"{}\" is a forward, and will not be handled directly by ThymeleafViewResolver.", viewName);
forwardUrl = viewName.substring("forward:".length(), viewName.length());
// 最終在renderMergedOutputModel方法 中調(diào)用servlet原生轉(zhuǎn)發(fā)requestDispatcher.forward
return new InternalResourceView(forwardUrl);
} else if (this.alwaysProcessRedirectAndForward && !this.canHandle(viewName, locale)) {
vrlogger.trace("[THYMELEAF] View \"{}\" cannot be handled by ThymeleafViewResolver. Passing on to the next resolver in the chain.", viewName);
return null;
} else {
vrlogger.trace("[THYMELEAF] View {} will be handled by ThymeleafViewResolver and a {} instance will be created for it", viewName, this.getViewClass().getSimpleName());
return this.loadView(viewName, locale);
}
}
}
9. 日期格式化
SpringBoot 默認(rèn)日期格式是yyyy/MM/dd
端壳,如果需要修改可以在 application.properties 中配置
spring.mvc.format.date=yyyy-MM-dd
10. 跳轉(zhuǎn)到員工編輯頁面
GET請求 /emp/{id}告丢,將該員工信息返回到前臺,由于還要顯示所有部門信息损谦,所以查詢所有部門岖免,返回信息無論添加到Map岳颇,還是ModelAndView都是一樣的。
返回頁面為 /emp/add.html觅捆,這是將添加與編輯功能混合起來的頁面
@GetMapping("/emp/{id}")
public String toeditPage(@PathVariable("id") Integer id, Map<String, Object> map) {
Employee employee = employeeDao.get(id);
map.put("employee", employee);
// 查詢部門赦役,返回到編輯頁面
Collection<Department> departments = departmentDao.getDepartments();
map.put("departments", departments);
return "/emp/add";
}
11. 編輯員工信息
PUT請求,/emp栅炒,返回編輯員工頁面
- 由于編輯員工與添加員工共用一個頁面add.html掂摔,需要在表單中加入 _method 屬性,當(dāng)編輯時生效
- 注意發(fā)送的仍然為 POST 請求赢赊,只是會攜帶參數(shù)
_method=PUT
乙漓,但是 SpringBoot 中的 HiddenHttpMethodFilter 過濾器會對隱藏的請求方式進(jìn)行修改,將請求修改為 PUT 請求释移。 - form 表單本身不支持 PUT DELETE 請求
- 注意url中沒有 /{id}
<input type="hidden" name="_method" value="PUT" th:if="${employee != null}">
開啟SpringBoot 中的 HiddenHttpMethodFilter 過濾器叭披,這樣才能將 POST 請求轉(zhuǎn)為 PUT
# 將POST請求轉(zhuǎn)換為PUT請求,springboot2.x默認(rèn)關(guān)閉
spring.mvc.hiddenmethod.filter.enabled=true
保存員工信息玩讳,
// 編輯員工信息
@PutMapping("/emp")
public String updateEmp(Employee employee) {
System.out.println("保存的員工信息" + employee);
// save 方法是employee沒有id涩蜘,則新增;有id則更新
employeeDao.save(employee);
return "redirect:/emps";
}
隱藏的請求方式轉(zhuǎn)換過濾器 HiddenHttpMethodFilter 源碼如下
HiddenHttpMethodFilter:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
// 查看是否為POST請求
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
// 獲取請求中攜帶的_method屬性
String paramValue = request.getParameter("_method");
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
// 如果method為 PUT 或 DELETE熏纯,則創(chuàng)建新的請求同诫,設(shè)置請求方式為method
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
12. 員工刪除
DELETE請求 /emp/{id},重定向到員工列表請求 /emps樟澜,返回員工列表頁面 /list.html
- 發(fā)送的還是 POST請求误窖,SpringBoot 會轉(zhuǎn)換為 DELETE 請求
// 刪除員工信息
@DeleteMapping("/emp/{id}")
public String deleteEmp(@PathVariable("id") Integer id) {
employeeDao.delete(id);
// 重定向到員工列表頁面,返回所有員工信息
return "redirect:/emps";
}
小知識: 不要從 pdf 復(fù)制代碼
查看下方兩個 form-control秩贰,上面是從pdf復(fù)制的代碼霹俺,下面是手動敲的代碼,看起來完全一致毒费,但是在代碼中替換會報(bào)錯丙唧。
查看二者的十六進(jìn)制編碼,發(fā)現(xiàn)上方-
的編碼是 E28090觅玻,下方-
編碼是 2D想际,對應(yīng) ASCII 碼表中的-
form‐control
form-control
00000000: 66 6F 72 6D E2 80 90 63 6F 6E 74 72 6F 6C 0D 0A formb..control..
00000010: 66 6F 72 6D 2D 63 6F 6E 74 72 6F 6C form-control
注:使用 VSCode 插件 Hexdump 查看的十六進(jìn)制編碼
4.7 定制錯誤頁面
1. SpringBoot 默認(rèn)錯誤處理機(jī)制
-
訪問一個不存在的url,會發(fā)生錯誤串塑,返回錯誤頁面沼琉。SpringBoot 默認(rèn)錯誤頁面如下
SpringBoot錯誤頁面 -
如果使用其他客戶端(Postman),則返回一個json數(shù)據(jù)
{ "timestamp": "2020-07-07T09:20:38.492+00:00", "status": 404, "error": "Not Found", "message": "", "path": "/crud/dsahw" }
原因:之所以二者返回結(jié)果不同桩匪,是因?yàn)闉g覽器請求錯誤頁面打瘪,請求頭中包含參數(shù)
Accept: text/html
,表示優(yōu)先接受 html 數(shù)據(jù),所以響應(yīng)返回html頁面闺骚。而postman 請求錯誤頁面請求頭參數(shù)為Accept: */*
彩扔,所以響應(yīng)返回json數(shù)據(jù)。
步驟:
- 當(dāng)系統(tǒng)發(fā)生 4xx 或 5xx 錯誤僻爽,ErrorPageCustomizer錯誤頁面響應(yīng)規(guī)則就會生效虫碉,就會轉(zhuǎn)發(fā)到 /error 請求
- /error 請求由 BasicErrorController 處理,返回 html 頁面或 json 數(shù)據(jù)
- 使用 DefaultErrorAttributes 獲取錯誤信息
- 將上一步獲取的錯誤信息添加到 ModelAndView中返回胸梆。如果返回 html 頁面敦捧,則 DefaultErrorViewResolver 根據(jù)狀態(tài)碼去 /error 目錄下查找對應(yīng)的 4xx.html 頁面并返回;
原理:
ErrorMvcAutoConfiguration碰镜,錯誤處理的自動配置兢卵,這個類給容器中添加了以下組件:
-
ErrorPageCustomizer:錯誤頁面配置信息
public class ErrorProperties { // 從配置文件讀取錯誤頁面路徑,默認(rèn)為/error // 類似與在web.xml中注冊的錯誤頁面 @Value("${error.path:/error}") private String path = "/error";
擴(kuò)展:在web.xml中配置錯誤響應(yīng)或異常對應(yīng)的頁面
https://blog.csdn.net/qq_41642093/article/details/79100579 -
BasicErrorController:處理/error請求
@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { // 響應(yīng)html類型的數(shù)據(jù)绪颖,瀏覽器請求來這個方法處理 @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); // 使用 DefaultErrorAttributes 獲取錯誤信息秽荤,并返回到頁面 Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); // 哪個頁面作為錯誤頁面?ModelAndView包含頁面地址和頁面數(shù)據(jù) ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } // 響應(yīng)json類型的數(shù)據(jù)柠横,postman來這個方法處理 @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL)); return new ResponseEntity<>(body, status); }
-
DefaultErrorAttributes:獲取錯誤頁面屬性集合
@Override public Map<String, Object> getErrorAttributes( RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>(); // 返回錯誤頁面展示的信息窃款,包括時間,狀態(tài)碼牍氛,錯誤信息晨继,請求路徑等 errorAttributes.put("timestamp", new Date()); addStatus(errorAttributes, requestAttributes); addErrorDetails(errorAttributes, requestAttributes, includeStackTrace); addPath(errorAttributes, requestAttributes); return errorAttributes; }
- DefaultErrorViewResolver:根據(jù)錯誤碼去 /error 下查找對應(yīng)的 4xx.html 并返回
@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())) { // 根據(jù)狀態(tài)碼去查找錯誤頁面 modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { // SpringBoot默認(rèn)去找錯誤頁面,路徑為 error/45xx.html String errorViewName = "error/" + viewName; TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); if (provider != null) { // 如果模板引擎可用糜俗,則返回并渲染錯誤頁面 return new ModelAndView(errorViewName, model); } // 模板引擎不可用踱稍,則去返回 static/45xx.html 靜態(tài)頁面 return resolveResource(errorViewName, model); }
2. 定制錯誤響應(yīng)頁面
- 定制錯誤頁面
- 創(chuàng)建錯誤頁面 /templates/error/404.xml曲饱,若發(fā)生 404 錯誤悠抹,模板引擎則渲染返回該頁面
- 也可以創(chuàng)建 4xx.html 來匹配 4xx 錯誤,精確優(yōu)先
- 頁面可以獲取的信息:
- timestamp 時間戳扩淀,
- status 錯誤狀態(tài)碼楔敌,
- error 錯誤提示,
- exception 異常對象驻谆,
- messgae 異常信息
- errors JSR303數(shù)據(jù)校驗(yàn)錯誤
- 如果模板引擎找不到匹配的錯誤頁面卵凑,則去靜態(tài)資源文件夾下查找并返回對應(yīng)的靜態(tài)頁面
- 如果以上都沒有,則使用SpringBoot默認(rèn)的錯誤頁面
在錯誤頁面顯示異常信息需要手動開啟:
# 返回異常信息到錯誤頁面
server.error.include-exception=true
server.error.include-message=always
在錯誤頁面顯示錯誤信息:
<!-- 行內(nèi)寫法與普通寫法胜臊,前者更簡便 -->
<h2>status: [[${status}]]</h2>
<h2 th:text="'timestamp: ' + ${timestamp}"></h2>
<h2>error: [[${error}]]</h2>
<h2>exception: [[${exception}]]</h2>
<h2>message: [[${message}]]</h2>
<h2>errors: [[${errors}]]</h2>
- 定制錯誤json數(shù)據(jù)
-
使用注解@ControllerAdvice 自定義異常處理器勺卢,發(fā)生異常后返回 json 數(shù)據(jù)
// 自定義異常處理器,需要注解 @ControllerAdvice @ControllerAdvice public class MyExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class); // 只處理UserNotExistException異常象对,返回json數(shù)據(jù)黑忱,包含錯誤碼code,異常信息message @ResponseBody @ExceptionHandler(UserNotExistException.class) public Map<String, Object> handlerUserNotException(Exception e) { logger.error("用戶不存在異常:{}", e); Map<String, Object> map = new HashMap<>(); map.put("code", "user.notexist"); map.put("message", e.getMessage()); return map; } }
-
轉(zhuǎn)發(fā)到/error進(jìn)行自適應(yīng)響應(yīng)效果處理,瀏覽器返回頁面甫煞,postman返回json
@ExceptionHandler(UserNotExistException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); //傳入我們自己的錯誤狀態(tài)碼 4xx 5xx菇曲, // 不傳入的話會訪問/error成功,狀態(tài)碼為200抚吠,就不返回錯誤頁面 5xx.html了 /** * BasicErrorController中獲取狀態(tài)碼如下: * Integer statusCode = (Integer) request .getAttribute("javax.servlet.error.status_code"); */ request.setAttribute("javax.servlet.error.status_code",500); map.put("code","user.notexist"); map.put("message","用戶出錯啦"); request.setAttribute("ext",map); //轉(zhuǎn)發(fā)到/error return "forward:/error"; }
4.8 配置嵌入式 Servlet 容器
SpringBoot 默認(rèn)使用的嵌入式 Servlet 容器為 Tomcat常潮,查看 pom 依賴,可以看到 web-starter 依賴 tomcat-starter楷力,使用的是 9.0 版本的 Tomcat喊式。
[圖片上傳失敗...(image-4ead43-1606212213489)]
1. 修改 Servlet 容器的相關(guān)配置
-
在
application.properties
中配置 Tomcat,屬性參考 ServerPropertiesserver.port=8090 server.servlet.context-path=/crud server.tomcat.max-connections=8 server.tomcat.uri-encoding=UTF-8
下面是 ServerProperties 源碼萧朝,配置的 server 屬性都綁定到該類屬性
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { // Tomcat端口垃帅,server.port private Integer port; // Servlet有屬性contextPath,表示項(xiàng)目路徑 server.servlet.context-path private final Servlet servlet = new Servlet(); // Tomcat有屬性uriEncoding剪勿,表示uri編碼 server.tomcat.uri-encoding private final Tomcat tomcat = new Tomcat();
-
自定義 WebServerFactoryCustomizer贸诚,配置 Tomcat 屬性,優(yōu)先級高于配置文件
參考 SpringBoot 服務(wù)器配置文檔(與SpringBoot 1.x 配置方式不同)厕吉,自定義 WebServerFactoryCustomizer酱固,設(shè)置Tomcat 端口,uri編碼等規(guī)則头朱。
@Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { // 配置 Tomcat运悲,將自定義配置加入容器 @Bean public WebServerFactoryCustomizer webServerFactoryCustomizer() { return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() { // 定制嵌入式Servlet容器相關(guān)的規(guī)則,對其他容器也生效 @Override public void customize(ConfigurableWebServerFactory factory) { factory.setPort(8090); } }; }
2. 注冊 Servlet 三大組件
請求處理過程: 一個請求進(jìn)入Tomcat项钮,需要經(jīng)過 Filter -> Servlet -> Interceptor -> Controller 四個步驟班眯,詳細(xì)如下:
Servlet、Filter烁巫、Listener 三大組件之前是配置在 web.xml 中署隘,SpringBoot默認(rèn)以 jar 包方式啟動嵌入式 Tomcat 來運(yùn)行 SpringBoot 的 web應(yīng)用,沒有 web.xml,所以使用以下方式注冊 Servlet 三大組件:
- 注冊自定義 Servlet,
- 使用 ServletRegistrationBean 注冊自定義 Servlet 組件到容器变勇,需要@Bean注解
- 這個Servlet不會被前面的攔截器攔截卓缰,因?yàn)樽远x的 MyServlet 與 DispatchServlet 平級,攔截器是在 DispatchServlet 后,對應(yīng) Controller 處理前生效的,所以不會攔截自定義的 MyServlet
- Spring的@Bean注解用于告訴方法,返回一個Bean對象脾还,將其加入Spring容器。產(chǎn)生這個Bean對象的方法Spring只會調(diào)用一次(單例)入愧。
// 自定義Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 返回 hello serlvet 給頁面
resp.getWriter().write(" hello serlvet...");
}
}
// @Configuration用于定義配置類鄙漏,可替換xml配置文件赛蔫,被注解的類內(nèi)部包含有一個或多個被@Bean注解的方法
// 這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進(jìn)行掃描,并將這些 Bean 加入到 Spring 容器
// 注冊自定義 Servlet 組件到容器
@Configuration
public class MyServerConfig {
// 注冊Servlet組件
@Bean
public ServletRegistrationBean myServlet() {
// 創(chuàng)建注冊器泥张,參數(shù)為MyServlet與映射路徑/mySerlvet
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/mySerlvet");
return registrationBean;
}
}
實(shí)際案例: SpringBoot 不需要在 web.xml 中配置 SpringMVC 的前端控制器 DispatcherServlet 呵恢,其自動注冊 DispatcherServlet,注冊方式就是使用 ServletRegistrationBean 將 DispatcherServlet 添加到容器
@Bean(name = {"dispatcherServletRegistration"})
@ConditionalOnBean(value = {DispatcherServlet.class}, name = {"dispatcherServlet"})
public ServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
// 創(chuàng)建注冊器媚创,參數(shù)為DispatcherServlet與映射路徑 /
// 可以通過 server.servlet-path 修改dispatcherServlet 的映射路徑
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet, new String[]{this.serverProperties.getServletMapping()});
registration.setName("dispatcherServlet");
// 設(shè)置啟動順序?yàn)?-1
registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.m ultipartConfig);
}
return registration;
}
- 注冊自定義過濾器 Filter
- 使用 FilterRegistrationBean 注冊自定義過濾器到容器渗钉,需要@Bean注解
- 過濾器會在指定url請求到 servlet 之前生效
- 過濾器由Servlet容器管理,而攔截器則可以通過IoC容器來管理
- 常見過濾器:編碼過濾器钞钙,敏感詞過濾器鳄橘,壓縮資源過濾器
// 自定義過濾器
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 過濾處理
System.out.println("MyFilter doFilter...");
// 調(diào)用過濾器鏈中的下一個過濾器
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
@Configuration
public class MyServerConfig {
// 注冊自定義過濾器到容器
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MyFilter());
// 設(shè)置過濾的url
registrationBean.setUrlPatterns(Arrays.asList("/myServlet"));
return registrationBean;
}
除了注冊過濾器的方式,還可以使用注解注冊過濾器 https://www.cnblogs.com/paddix/p/8365558.html
常見的過濾器實(shí)現(xiàn)參考芒炼,包括編碼過濾器瘫怜,敏感詞過濾器,壓縮資源過濾器 https://mp.weixin.qq.com/s/psRMhj4IlcjyVPE0a64vBA
- 注冊自定義監(jiān)聽器
- 使用 ServletListenerRegistrationBean 注冊自定義監(jiān)聽器到容器本刽,需要@Bean注解
- 常見監(jiān)聽器:網(wǎng)站訪問人數(shù)統(tǒng)計(jì)
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized...web應(yīng)用啟動");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextInitialized...web應(yīng)用銷毀");
}
}
// 注冊Listener到容器
@Bean
public ServletListenerRegistrationBean myListener() {
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
}
過濾器與監(jiān)聽器都可以參考 https://github.com/ZhongFuCheng3y/3y
使用其他嵌入式 Servlet 容器
SpringBoot 支持 3 種嵌入式 Servlet 容器:
- Tomcat 默認(rèn)
- Jetty 長鏈接友好
- Undertow 非阻塞式鲸湃,并發(fā)性能好
切換為 Jetty: 修改 pom.xml 配置文件,移除 tomcat-starter子寓,引入 jetty-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 前面排除默認(rèn)依賴的tomcat暗挑,現(xiàn)在引入jetty-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
重新啟動項(xiàng)目,Jetty started on port(s) 8080 (http/1.1) with context path '/crud'
切換為 Undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 前面排除默認(rèn)依賴的tomcat斜友,現(xiàn)在引入undertow-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
小技巧: IDEA 排除 pom.xml 中的依賴包
右擊 pom.xml炸裆,選擇 Diagram,找到要排除的包鲜屏,右擊選擇 Exclude
4.9 嵌入式 Servlet 容器自動配置原理
// 補(bǔ)充:p49烹看,SpringBoot 2.x 這塊變動較大
4.10 嵌入式 Servlet 容器啟動原理
// 補(bǔ)充:p50,SpringBoot 2.x 這塊變動較大洛史。這兩個章節(jié)是面試重點(diǎn)惯殊,但是并沒有搞懂
4.11 使用外置的 Servlet 容器
-
嵌入式Servlet容器:應(yīng)用打成可執(zhí)行的jar
- 優(yōu)點(diǎn):簡單、便攜虹菲;
- 缺點(diǎn):默認(rèn)不支持JSP靠胜、優(yōu)化定制比較復(fù)雜
外置的Servlet容器:外面安裝Tomcat掉瞳,將應(yīng)用打?yàn)閣ar包并部署毕源;
// 補(bǔ)充:流程與原理