導讀
篇幅較長,干貨十足,閱讀需花費點時間。珍惜原創,轉載請注明出處,謝謝!
Spring Boot基礎
Spring Boot簡介
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力於在蓬勃發展的快速應用開發領域(rapid application development)成為領導者。
簡單來說,SpringBoot可以簡化應用程序的開發,使我們不再需要spring配置文件及web.xml。
SpringBoot工程和創建
IDEA中的創建




工程編輯
系統會在前面設置的包中自動生成一個啟動類。

在啟動類所在的包下再創建一個子包,在其中編寫SpringMvc的處理器類。
注意,要求代碼所在的包必須是啟動類所在的包的子孫寶,不能是同級包。

啟動
ide方式

mac控制台方式(需maven打包)

官網創建
配置及生成

配置項配置完成后,點擊Generate按鈕后,即可打開一個下載對話框。官網將配置好的Spring Boot工程生成一個zip壓縮文件,只要我們將下載后的文件解壓並添加到工程即可使用。
基於war的Spring Boot工程
前面2種方式創建的Spring Boot工程最終被打為了jar包,是以可執行文件的形式出現的,他們都使用了Spring Boot內嵌的Tomcat作為web服務器來運行web應用的。新版的Dubbo的監控中心工程就是典型的應用。但在實際生產環境下,對於web工程,很多時候我們需要的是war包,然后部署到企業級web服務器中。下面來演示如何使用Spring Boot將工程打為war包。
工程創建

我們看一下pom.xml文件,可以知道,Tomcat打包的時候不打進去

將項目打包后,仍到Tomcat的wabapps目錄下

啟動Tomcat

接下來我們查看Tomcat目錄

打開網頁,注意加上項目名


注:mac啟動Tomcat過程中遇到點麻煩,1、需要賦予文件夾權限;2、執行shell腳本報錯:Operation not permitted,博主參考了下面2個鏈接配成成功噠
大概配置步驟
1、切換Tomcat的bin目錄下 sudo chmod 755 *.sh 2、解決Operation not permitted xattr -d com.apple.quarantine ./* 3、啟動Tomcat sudo sh ./startup.sh
4、停止Tomcat
sudo sh ./shutdown.sh
參考鏈接: 注:第一、二鏈接,賦權限,第三個鏈接解決Operation not permitted
https://blog.csdn.net/caoxiaohong1005/article/details/53463443
https://blog.csdn.net/F_Feng0628/article/details/60583250
https://blog.csdn.net/default7/article/details/80172340
工程解析
創建Spring Boot工程后,會自動生成工程的啟動類Application。

跟蹤@SpringBootApplication注解源碼可以看到,可以完成Spring Boot的自動配置。

繼續跟蹤@SpringBootConfiguration源碼可以看到,其本質是一個@Configuration,使用Java類充當Spring配置文件。

src/main/resources目錄

- static:存放靜態資源,如:css,js,images等
- templates目錄:存放動態資源。Spring Boot不建議使用jsp作為動態數據展示頁面,而是建議使用Thymeleaf。Thymeleaf是一種Java模板引擎,可以顯示動態數據。Thymeleaf文件的擴展名為html,是對html的擴展。該目錄用於存放Thymeleaf文件。
- 這兩個目錄中存放的資源都相當於存放當前web應用的根下。一般不適用他們存放資源。
- application.properties:SpringBoot的主配置文件
查看當前工程
在pom.xml文件中包含SpringBoot的兩個核心依賴及一個核心插件。一個是SpringBoot的Web啟動依賴,一個是SpringBoot的測試啟動依賴;而這個核心插件是用於完成對SpringBoot工程打包結果的再打包。
注:他們都沒有版本號,這是為什么呢?

雖然他們都沒有版本號,打開maven可以看到,他們版本均為2.2.2

他們的版本號均繼承自父工程。從pom文件中可以看到,其父工程為spring-boot-starter-parent。



更換默認springframework的版本(視生產環境降默認版本)

查看spring-boot-starter-parent工程
打開Maven本地倉庫,在org.springframework中找到工程spring-boot-starter-parent。

打開spring-boot-starter-parent-2.2.2.RELEASE.pom文件,發現該工程的<pluginManagement>中有對該插件的配置信息。
注:<pluginManagement>中聲明的插件是選擇性繼承插件,而非全部繼承插件。

從該插件的配置信息可以得出,其執行的目標是repackage,即對工程進行重新打包。
首次打包是由Maven的package插件完成,其打包的結果僅僅是將當前工程中自定義的類打包成了一個普通的jar包,沒有Spring Boot的相關資源,無法直接運行。當然,其打包的結果是.jar文件。
重新打包是由spring-boot-maven-plugin插件完成的,其實將前面打成的jar包進行了二次打包,將原來的軟件包變為了Spring Boot可執行包,在其中不僅添加了Spring Boot依賴,還添加了很多其他配置。同時,其還將原來的.jar文件擴展名變為了.orininal,而而二次打包后的文件擴展名成為了.jar。

可以打開01-primary-0.0.1-SNAPSHOT.jar

BOOT-INF目錄:存放的是當前應用程序的類及其所依賴的類
META-INF目錄:存放的是Maven相關的配置文件
org目錄:存放的是SpringBoot啟動所需的類
打開META-INF/MANIFEST.MF,不僅可以看到主類Main-Class,還可以看到當前工程的啟動類Start-Class。

maven打包
Spring Boot熱部署
idea中的Spring Boot工程若要使用熱部署,需要2不操作:
- 導入devtools依賴
- 編輯工程的配置信息
注:Spring Boot工程在idea和Eclipse中的重啟時機不同。Eclipse只要修改的文件保存過了,工程會立馬重新部署。而idea則對文件修改后的保存是自動的,所以其重新部署的時機是idea整個ide窗口被鈍化時,即窗口切換到其他窗口時,工程會重新部署。
添加依賴
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
配置

當idea被鈍化時,即當idea窗口被切換時,更新類與資源。
在開發調試過程中,已經啟動了工程的運行。此時又修改了代碼或配置文件,若要使修改生效,則需要重新啟動工程。這種方式降低了開發效率。
熱部署,即修改了代碼或配置文件后,一旦保存,系統馬上對工程進行自動重啟,無需手工重啟。若要實現熱部署,只需要在pom.xml中添加一個依賴即可。
熱部署的使用有利有弊。利:無需手動重啟工程;弊:在修改代碼或配置后,只要保存系統就會重啟工程,即使這個修改還未完畢,其他的會重啟,從而導致代碼重啟后報錯。
1、添加依賴
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2、設置自動生成項目

3、Ctrl+Shift+A,打開搜索框,輸入:Registry

4、勾選即可

Spring Boot主配置文件
配置文件形式一
Spring Boot的主配置文件是src/main/resources中默認創建的application.properties文件。
編輯配置文件

啟動

注意事項
注:這里指定的Tomcat端口號,僅僅只針對內置Tomcat的,是測試時使用的。將工程打為war包后部署到真正的Tomcat,這些配置是不起作用的。
配置文件形式二
Spring Boot的主配置文件也可以使用application.yml文件。.yml也可以寫成.yaml。
在開發之初YAML的本意是Yet Another Markup Language(仍是一種標記語言)。后來為了強調這種語言是以數據為中心,而不是以標記為中心,所以將標記為中心,所以將YAML解釋為Yaml Ain't Markup Language(Yaml不是一種標記語言)。它是一種直觀的能夠被電腦識別的數據序列化格式,是一個可讀性高並且容易被人閱讀,容易和腳本語言交互,用來表達多級資源序列的編程語言。
yml與properties文件的主要區別是對於多級屬性,即key的顯示方式不同。yml文件在輸入時,只需按照點(.)的方式輸出key既可,輸入完畢后回車即出現了如下形式。該形式要求冒號后與值之間有一個空格(語法格式)。

注意事項
注:application.properties與application.yml這兩個文件只能有一個。要求文件名必須是:application。
Actuator
Actuator是Spring Boot提供的對應用系統的自省和監控的集成功能,可以對應用系統進行配置查看、相關功能統計等。在Spring Colud中主要是完成微服務的監控,完成監控治理。可以查看微服務間的數據處理和調用,當它們之間出現了異常,就可以快速定位到出現問題的地方。
其功能與Dubbo的監控中心類似,不同的是,Dubbo的監控中心是需要專門部署的,而Spring Boot的Actuator是存在於每個工程中。
環境搭建
隨便一個Spring Boot工程中都可以使用Actuator對其進行監控。
添加依賴
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
修改配置文件
application.properties
# 當前應用的端口與上下文路徑 server.port=8888 server.servlet.context-path=/cyb # Actuator監控的端口號與上下文路徑 management.server.port=9999 management.server.servlet.context-path=/cyb2 # 指定監控終端的基本路徑,默認為actuator management.endpoints.web.base-path=/base
啟動項目並測試

添加Info信息
修改配置文件
在配置文件中添加如下Info信息,則可以通過Info控制終端查看到。

測試

注:博主瀏覽器安裝了JSON格式化工具,所以顯示的是JSON格式化后的狀態

開發其他監控終端
默認情況下,Actuator僅開放了health與Info兩個監控終端,但它還有很多終端可以監控,需手動開放。
修改配置文件

mappings終端
mappings可以看到當前工程中所有的URI與處理器的映射關系,及詳細的處理器方法以及其映射規則。很實用!!!

beans終端

evn終端
可以看到當前應用程序運行主機的所有軟硬件環境信息。

單獨關閉某些監控終端
在開放了所有監控終端的情況下,有些終端顯示的信息並不想公開,此時可以單獨關閉這些終端。
修改配置文件

測試
其他的監控終端不受影響。

其他常用的監控終端如下
若沒有你需要的,可以自行百度搜“springboot actuator”


Spring Boot重要用法
自定義異常頁面
對於404、405、500等異常頁面,而這些異常頁面一般都是英文的,非常不友好。我們可以通過簡單的方式使用自定義異常頁面,並將默認狀態碼頁面進行替換。
定義目錄
在src/main/resources目錄下新建目錄:public/error。
定義異常頁面
在error目錄中定義異常頁面。這些異常頁面的名稱必須為相應的狀態碼,擴展名為html。

單元測試
添加依賴
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency>
接口類
ISomeService.java
package com.cyb.webdemo.controller; public interface ISomeService { void doSome(); }
接口實現類
SomeServiceImpl.java
package com.cyb.webdemo.controller; import org.springframework.stereotype.Service; @Service public class SomeServiceImpl implements ISomeService{ @Override public void doSome() { System.out.println("執行Service的doSome"); } }
測試類
類上需添加兩個注解:
1、@RunWith(SpringRunner.class)
2、@SpringBootTest(classes = Spring Boot啟動類.class)

多環境選擇
什么是多環境選擇?
在開發應用時,通常同一套程序被運行在多個環境,例如,開發、測試、生產環境等。每個環境的數據庫地址、服務器端口號等配置都會不同。若在不同環境下運行時將配置文件修改為不同內容。那么,這種做法不僅非常繁瑣,而且很容易發生錯誤。
在開發應用時,有時不同的環境,其需要運行的接口的實現類也是不同的。例如,若要開發一個具有短信發送功能的應用,開發環境中要執行的send()方法僅需要調用短信模擬器即可,而生產環境中要執行的send()則需要調用短信運營商所提供的短信發送接口。這種情況下,就需要開發兩個相關接口的實現類去實現send()方法。
對於不同的環境,需要使用不同的配置文件,執行不同的類。而這個選擇只需要在Spring Boot的主配置文件中指定即可。
下面以不同的環境“使用配置有不同的端口號的配置文件,以及調用不同的接口實現類”為例演示多環境選擇問題的解決。
Example
1、創建幾個配置文件,格式:application-xxxxx.properties



2、創建一個包,並創建一個接口,內容如下

2、創建DevMsgServiceImpl開發環境類
注:@Profile("xxx")中的值要和application-xxx.properties對應上

3、創建ProMsgServiceImpl生產環境
注:@Profile("xxx")中的值要和application-xxx.properties對應上

4、創建處理器:MsgController

5、測試
正確請求

錯誤請求

說明
在Spring Boot中多環境配置文件名需滿足:application-[profile].properties的格式,其中[profile]為對應的環境標識,例如
- application-dev.properties:開發環境
- application-pro.properties:生產環境
至於那個配置文件會被加載,取決於application.properties中的spring.profiles.active屬性來設置,其值對應[profile]。例如spring.profiles.active=dev就會加載application-dev.properties配置文件內容。
在實現類上添加@Profile注解,並在注解參數中指定前述配置文件中的[profile]值,用於指定該實現類所使用的環境。
以上開發過程中碰到的問題都解決啦~~~~
6、在命令行下選擇環境
將工程打為Jar包后,在命令行運行。若要想切換運行環境,必須要修改主配置文件嗎?答案是否定的。只需添加一個命令參數即可動態指定。
例如,現在的主配置文件中指定的是dev環境

將當前工程打為Jar包后,在命令行運行時添加如下參數

此時執行的就是生產環境,調用的就是ProduceServiceImpl類

讀取自定義配置
自定義配置,可以是定義在主配置文件:application.properties中的自定義屬性,也可以是自定義配置文件中的屬性。
讀取主配置文件中的屬性
application.properties

SomeController.java

實現

讀取指定配置文件中的屬性
創建配置文件:cybApplication.properties

SomeController.java 
實現

讀取對象屬性
cybApplication.properties

實體類:cyb.java

說明
- @PropertySource:用於指定要讀取的配置文件。
- @ConfigurationProperties:用於指定要讀取配置文件中的對象屬性。
- @Component:當前從配置文件讀取來的對象,由Spring容器創建。
SomeController.java

實現

讀取List屬性1
cybApplication.properties

實體類:UserName

SomeController.java

實現

讀取List屬性2
cybApplication.properties

Group.java

students.java

SomeController.java

實現

補充知識(lombok)
lombok方式配置實體類。沒用過的小伙伴,可以參考Lombok介紹、使用方法和總結進行腦補,這里只演示使用
1、idea安裝lombok

2、添加依賴
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
3、實體類上加@Data即可
package com.cyb.webdemo.controller; import lombok.Data; @Data public class studens { private String name; private int age; // public String getName() { // return name; // } // // public void setName(String name) { // this.name = name; // } // // public int getAge() { // return age; // } // // public void setAge(int age) { // this.age = age; // } }
注:lombok省去了get/set,讓代碼更簡潔。
Spring Boot下使用JSP頁面
添加JSP解析依賴
pom.xml
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency>
創建webapp目錄
在src/main下創建webapp目錄,用於存放jsp文件。創建的是一個普通目錄,需要指定為web資源目錄,然后才可以創建jsp文件。File->Project Structure


添加JSP注冊資源目錄
pom.xml
<build> <resources> <!--注冊dao包下mybatis映射文件為資源目錄--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--注冊webapp目錄為資源目錄--> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> <includes> <include>**/*.*</include> </includes> </resource> </resources> </build>
創建JSP頁面

實現
創建welcome.jsp

創建home.jsp

SomeController.java

實現

關於靜態資源請求
Spring Boot已經處理好了靜態資源訪問問題。
視圖前/后綴

Spring Boot中使用MyBatis
添加依賴項
MyBatis與Spring Boot整合依賴
pom.xml
<!--Mybatis與Spring Boot整合依賴,必須要版本號--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
MySql驅動依賴
pom.xml
<!--mysql驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
Druid連接池依賴
pom.xml
<!--Druid連接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
lombok依賴(可忽略,用於實體類)
pom.xml
<!--lombok依賴--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
注:若使用lombok需配置ide。
注冊資源目錄文件
pom.xml
<build> <resources> <!--注冊dao包下mybatis映射文件為資源目錄--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--注冊webapp目錄為資源目錄--> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> <includes> <include>**/*.*</include> </includes> </resource> </resources> </build>
注:若不注冊dao包下的mybatis映射文件,需將xxx.xml文件放到:/resource,並且修改application.properties文件中的mybatis.mapper-locations映射路徑!!!!!!!
資源文件
application.properties
# 視圖的前綴和后綴
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
# 注冊映射文件
mybatis.mapper-locations=classpath:com/cyb/webdemo/dao/EmployeeDao.xml
# 注冊實體類別名
mybatis.type-aliases-package=com.cyb.webdemo.bean.EmployeePo
# 注冊數據源類型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 數據庫連接字符串
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/cyb
spring.datasource.username=root
spring.datasource.password=root
創建jsp
index.jsp

實體類(Po)
EmployeePo.java

注意
案例中使用了lombok依賴(idea中需要配置,可以參考前面講解lombok配置環境方法),所以實體類中可以不設置get/set方法。
dao層(數據訪問層)
EmployeeDao.java

EmployeeDao.xml

注:mybatis映射文件的存放位置!!!!!
代碼拷貝區
package com.cyb.webdemo.dao; import com.cyb.webdemo.bean.EmployeePo; import org.apache.ibatis.annotations.Mapper; @Mapper public interface EmployeeDao { void insertEmployee(EmployeePo employeePo); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cyb.webdemo.dao.EmployeeDao"> <insert id="insertEmployee"> insert into employee (name,age) values (#{name},#{age}) </insert> </mapper>
service層(業務層)
EmployeeService.java(接口)

EmployeeServiceImpl.java(接口實現類)

代碼拷貝區
package com.cyb.webdemo.service; import com.cyb.webdemo.bean.EmployeePo; public interface EmployeeService { void addEmployee(EmployeePo employee); }
package com.cyb.webdemo.service; import com.cyb.webdemo.bean.EmployeePo; import com.cyb.webdemo.dao.EmployeeDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class EmployeeServiceImpl implements EmployeeService{ @Autowired(required = false) private EmployeeDao dao; @Override public void addEmployee(EmployeePo employee) { dao.insertEmployee(employee); } }
Controller層
package com.cyb.webdemo.controller; import com.cyb.webdemo.bean.EmployeePo; import com.cyb.webdemo.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class RegisterHandler { @Autowired(required = false) private EmployeeService service; @PostMapping("register") public String RegisterHandler(EmployeePo employee){ service.addEmployee(employee); return "ok"; } }
掃描dao層(可忽略)

數據庫表結構

實現

項目源碼
Spring Boot事務支持
步驟
- 在啟動類上添加@EnableTransactionManagement
注解,開啟事務 - 在Service實現類的方法上添加@Transactional
注解
啟動類

業務層接口實現類

Spring Boot對日志的控制
logback日志技術
Spring Boot中使用日志技術為logback。其與Log4J都出自一人,性能要優於Log4J,是Log4J的替代者。
在Spring Boot中若要使用logback,則需要具有spring-boot-starter-logging依賴,而該依賴被spring-boot-starter-web依賴,即不用直接導入spring-boot-starter-logging依賴(間接依賴)。

方式一
xml方式
必須放到src/main/resources類路徑下!!!。

代碼拷貝區
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appender name="myConsole" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-5level - %msg%n</pattern> </encoder> </appender> <root level="WARN"> <appender-ref ref="myConsole"></appender-ref> </root> <logger name="com.cyb.webdemo.dao.EmployeeDao" level="DEBUG"></logger> </configuration>
方式二
application.properties

代碼拷貝區
application.properties
# 控制日志顯示格式 logging.pattern.console=%leven %msg%n logging.level.root=warn # 格式:logging.level.dao層命名空間 logging.level.com.cyb.webdemo.dao.EmployeeDao=debug
實現

Spring Boot中使用Redis(解決高並發,重點!!)
注意
案例從頭到尾,使用都是同一個項目,代碼配置結構復雜,看紅色圈中的代碼,敲黑板划重點!!!!!
學前預習
Redis還不會的小伙伴,請先預習:分布式架構-Redis 從入門到精通 。
應用場景
使用Redis緩存的數據划分為兩類:
- DB表中的數據更新后,Redis緩沖的相關數據要清除,要不然客戶端獲取的不是最新數據。
- 對數據准確性要求不高的數據,可以與DB表中的數據不一致,但差別不能太大,所以該類數據一般會設置過期1時效。
啟動redis服務器

Spring Boot與Redis整合依賴
完整pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cyb</groupId> <artifactId>webdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>webdemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>13</java.version> </properties> <dependencies> <!--Mybatis與Spring Boot整合依賴,必須要版本號--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--mysql驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--Druid連接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--lombok依賴--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--Spring Boot與redis依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <resources> <!--注冊dao包下mybatis映射文件為資源目錄--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--注冊webapp目錄為資源目錄--> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> <includes> <include>**/*.*</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
修改主配置文件
application.properties

代碼拷貝區
# 視圖的前綴和后綴
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
# 注冊映射文件
mybatis.mapper-locations=classpath:com/cyb/webdemo/dao/EmployeeDao.xml
# 注冊實體類別名
mybatis.type-aliases-package=com.cyb.webdemo.bean.EmployeePo
# 注冊數據源類型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 數據庫連接字符串
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/cyb
spring.datasource.username=root
spring.datasource.password=root
# 控制日志顯示格式
logging.pattern.console=%leven %msg%n
logging.level.root=warn
# 格式:logging.level.dao層命名空間
logging.level.com.cyb.webdemo.dao.EmployeeDao=debug
# 連接redis
spring.redis.host=192.168.31.200
spring.redis.port=6379
spring.redis.password=root
# 連接redis集群,redis利用哨兵機制實現高可用
# spring.redis.sentinel.master=mymaster
# spring.redis.sentinel.nodes=sentinel1:6370,sentinel2:6371,sentinel3:6372
# 指定緩存類型
spring.cache.type=redis
# 設置緩存名稱
spring.cache.cache-names=realTimeCache
JSP頁面
index.jsp

代碼拷貝區
<%-- Created by IntelliJ IDEA. User: chenyanbin Date: 2020/1/6 Time: 7:13 下午 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="register" method="post"> 姓名:<input type="text" name="name"><br/> 年齡:<input type="number" name="age"><br/> <input type="submit" value="注冊"> </form> <hr> <form action="find" method="post"> id:<input type="text" name="id"> <input type="submit" value="查詢"> </form> <a href="count">查看員工總數</a> </body> </html>
實體類
EmployeePo.java

數據庫表字段

Dao層(數據訪問層)
EmployeeDao.java

映射文件
EmployeeDao.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cyb.webdemo.dao.EmployeeDao"> <insert id="insertEmployee"> insert into employee (name,age) values (#{name},#{age}) </insert> <select id="selectEmployeeById" resultType="com.cyb.webdemo.bean.EmployeePo"> select id,name,age from employee where id=#{id} </select> <select id="selectEmployeeCount" resultType="int"> select count(1) from employee </select> </mapper>
Service(業務層)
EmployeeService.java(接口)

EmployeeServiceImpl.java(接口實現類)
注:雙重檢測鎖非常重要!!!!

代碼拷貝區
package com.cyb.webdemo.service; import com.cyb.webdemo.bean.EmployeePo; import com.cyb.webdemo.dao.EmployeeDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.concurrent.TimeUnit; @Service public class EmployeeServiceImpl implements EmployeeService { @Autowired(required = false) private EmployeeDao dao; @CacheEvict(value = "realTimeCache", allEntries = true) // 清除緩存,實際生產環境就這樣玩,因為每個實體類設置不同的緩存區空間,范圍小,清除緩存好操作 @Transactional @Override public void addEmployee(EmployeePo employee) { dao.insertEmployee(employee); } @Cacheable(value = "realTimeCache", key = "'employee_'+#id") // 先會去緩存中查下key是否存在,存在:則直接拿緩存中的數據;不存在:去數據庫中查,查完將結果放入緩存中 @Override public EmployeePo findEmployeeById(int id) { return dao.selectEmployeeById(id); } @Autowired private RedisTemplate<Object, Object> redisTemplate; //使用雙重檢測鎖,解決熱點緩存問題 //雙重檢測鎖,解決了高並發下,對數據庫訪問的壓力!!!! //熱點緩存腦補,請參考:https://www.jianshu.com/p/6e37a1a9c160 @Override public int findEmployeeCount() { //獲取Redis操作對象 BoundValueOperations<Object, Object> ops = redisTemplate.boundValueOps("count"); //從緩存中讀取數據 Object count = ops.get(); if (count == null) { synchronized (this) { count = ops.get(); if (count == null) { //從DB中查詢 count = dao.selectEmployeeCount(); //將查詢的數據,寫入Redis緩存,並設置到期時限 ops.set(count,10, TimeUnit.SECONDS); } } } return (int) count; } }
實現

注:細心的小伙伴發現,一個問題,根據id查詢數據庫的時候,為什么控制台沒有打印sql語句呢???對表進行增刪改時,清除緩存,查詢時設置緩存,因為之前演示的時候,已經將緩存寫入到redis服務器中了,查詢的時候,拿的是緩存數據!!

完整項目
Redis自動生成key(高級用法)
自動生成類
RedisCacheConfig.java
package com.cyb.webdemo.controller; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Configuration; @Configuration public class RedisCacheConfig extends CachingConfigurerSupport { // 自定義生成key結構:類名_方法名_參數值 // 本例:EmployeeServiceImpl_findEmployeeById_1 public KeyGenerator keyGenerator() { return (target, method, params) -> { //獲取注解所標的方法所在類的類名 String className = target.getClass().getName(); //獲取注解所標注的方法的方法名 String methodName = method.getName(); return className + "_" + methodName + "_" + params[0].toString(); }; } }

實現

Spring Boot攔截器
在非Spring Boot工程中若要使用Spring Mvc的攔截器,在定義好攔截器后,需要在Spring配置文件中對其進行注冊。但在Spring Boot工程中沒有Spring的配置文件,那么如何使用攔截器呢?
Spring Boot對於原來在配置文件配置的內容,現在全部體現在一個類中,該類需要繼承WebMvcConfigurationSupport類,並使用@Configuration進行注冊,表示該類為一個JavaConfig/codeConfig類,充當配置文件的角色。
定義攔截器
SomeInterceptor.java
package com.cyb.webdemo.controller; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class SomeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("攔截器:"+request.getRequestedSessionId()); return true; } }
注:返回值 true:不攔截;false:攔截
配置文件類
MyMvcConfiguration.java
package com.cyb.webdemo.controller; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration //表示該類充當配置文件 public class MyMvcConfiguration extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { SomeInterceptor someInterceptor = new SomeInterceptor(); registry.addInterceptor(someInterceptor) .addPathPatterns("/one/**") //攔截one開頭請求 .excludePathPatterns("/two/**"); //允許two開頭的請求 } }
注:**:代表多級目錄;*:只有一級目錄;
一般設置全部攔截addPathPatterns("/**");再設置局部的不攔截excludePathPatterns("/xx/**")
定義處理器
package com.cyb.webdemo.controller; import com.cyb.webdemo.bean.EmployeePo; import com.cyb.webdemo.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller public class SomeController { @RequestMapping("/two/index") public String RegisterHandler(){ return "two"; } @RequestMapping("/one/index") public String RegisterHandler2(){ return "one"; } }
Spring Boot中使用Servlet
在Spring Boot中使用Servlet,根據Servlet注冊方式的不同,有兩種使用方式。若使用的是Servlet3.0+版本,則兩種方式均可使用;若使用的是Servlet2.5版本,則只能使用配置類方式。
注解方式
若使用Servlet3.0+版本,可以使用注解方式,分兩步。
- 在定義好的Servlet上使用@WebServlet注解
- 在入口類上添加@ServletComponentScan注解
cybServlet.java
package com.cyb.webdemo.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "/cyb") public class cybServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("hello spring boot servlet!"); } }
入口類
WebdemoApplication

實現

配置類方式
若使用的是Servlet2.5版本,沒有Servlet注解,此時只能使用配置類方式。其總步驟有兩步,無需再入口類上添加@ServletComponentScan注解
定義Servlet
package com.cyb.webdemo.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class SomeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().println("servlet"); } }
定義配置類
package com.cyb.webdemo.servlet; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration //表示其為配置類,相當於applicationContext.xml文件 public class MyApplicationContext { @Bean //表示該方法返回的對象即為Spring容器中的Bean,方法名隨意 public ServletRegistrationBean<SomeServlet> getServletBean(){ // 創建Servlet SomeServlet someServlet=new SomeServlet(); // 注冊Servlet return new ServletRegistrationBean<SomeServlet>(someServlet,"/cyb"); } }
實現

Spring Boot中使用Filter
在Spring Boot中使用Filter與前面的使用Servlet相似,根據Filter注冊方式的不同,有兩種使用方式。若使用的是Servlet3.0+版本,則兩種方式均可使用;若使用的是Servlet2.5版本,則只能使用配置類方式。
注解方式
若使用的是Servlet3.0+版本,可以直接使用Filter的注解對Filter進行注冊。有兩步
- 在定義好的Filter上使用@WebFilter注解
- 在入口類添加@ServletComponentScan注解
SomeFilter.java
package com.cyb.webdemo.file; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") public class SomeFilter 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("信息已被過濾"); chain.doFilter(request,response); } @Override public void destroy() { } }
修改入口類
package com.cyb.webdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cache.annotation.EnableCaching; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement //開啟事務 @EnableCaching //開啟緩存 @ServletComponentScan("com.cyb.webdemo.file") //注意是包名 public class WebdemoApplication { public static void main(String[] args) { SpringApplication.run(WebdemoApplication.class, args); } }
配置方式
若使用的是Servlet2.5版本,沒有Filter注解,此時只能使用配置類方式。有兩步,與@ServletComponentScan注解無關。
定義Filter
package com.cyb.webdemo.file; import javax.servlet.*; import java.io.IOException; public class SomeFilter 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("信息已被過濾"); chain.doFilter(request, response); } @Override public void destroy() { } }
修改配置類
package com.cyb.webdemo.file; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyFilter { @Bean public FilterRegistrationBean<SomeFilter> getFilterBean(){ FilterRegistrationBean<SomeFilter> registrationBean=new FilterRegistrationBean<SomeFilter>(new SomeFilter()); registrationBean.addUrlPatterns("/*"); return registrationBean; } }
Thymeleaf
Thymeleaf,百里香葉。Thymeleaf是一個流行的模板引擎,該模板引擎采用Java語言開發。模板引擎為了使用戶界面與業務數據(內容)分離而產生,它可以生成特定格式的文檔。例如,用於網站的模板引擎就會生成一個標准的HTML文檔。不同的語言體系中都有自己的模板引擎,例如,Java中常見的模板引擎有Velocity、Freemaker、Thymeleaf等。不同的模板引擎都會具有自己的特定的標簽體系,而Thymeleaf以HTML標簽為載體,在HTML的標簽下實現對數據的展示。
Thymeleaf本身與Spring Boot沒有關系,但Spring Boot官方推薦使用Thymeleaf作為前端頁面的數據展示技術,Spring Boot很好地集成了這種模板技術。
Spring Boot集成Thymeleaf
創建項目

配置文件
application.properties

處理器

index.html
在頁面的<html>標簽中添加Thymeleaf的命名空間屬性:
<html lang="en" xmlns:th="https://www.thymeleaf.org/">

實現

Thymeleaf標准表達式
常用的Thymeleaf標准表達式有三種。標准表達式都是用於獲取代碼中存放到Model中的屬性值,只不過獲取方式不同罷了。
變量表達式:${...}
使用${...}的表達式,稱為變量表達式。該表達式的內容會顯示在HTML標簽體文本處。
該表達式一般都是通過th:text標簽屬性進行展示的。
實體類(Po)

處理器

index.html
方式一,直接使用實體類屬性

方式二:使用實體類get方法獲取值

注:兩種獲取值的方式效果都是一樣的。
實現

選擇表達式:*{...}
選擇表達式,也稱為星號表達式。一般用於展示對象的屬性。該表達式的內容會顯示在HTML標簽體文本處。但其需要與th:object標簽屬性聯用,先使用th:object標簽選擇了對象,再使用*{...}選擇要展示的對象屬性。該表達式可以有效降低頁面中代碼的冗余。
也可以不與th:object聯用,在*{...}中直接使用“對象.屬性”方式,這種寫法與變量表達式相同。
該表達式一般都是通過th:text標簽屬性進行展示。
index.html修改

實現

URL表達式:@{...}
其中只能寫一個絕對URL或相對URL地址的表達式,稱為URL表達式。這個絕對/相對URL地址中一般是包含有動態參數的,需要結合變量表達式${...}進行字符串拼接。
@{...}中的URL地址有3種寫法。
以Http協議開頭的絕對地址
在進行字符串拼接時使用“+”連接,容易出錯。但使用雙豎線則無需字符串拼接,簡單易讀。但是,在idea中的問號會報錯,不影響程序運行,可能是idea的bug吧。
index.html

查看網頁源碼

以/開頭的相對地址
在URL表達式中,Thymeleaf會將/解析為當前工程的上下文路徑ContextPath,而瀏覽器會自動為其添加"http://主機名:端口號",即為一個絕對路徑

不以/開頭的相對地址

查看頁面源碼可以看到解析結果是沒有添加任何東西的,沒有上下文路徑,其實相對於當前請求路徑的一個絕對地址。
自行學習
授人以魚不如授人以漁,其他具體的使用用法,請參照官網API:https://www.thymeleaf.org/documentation.html

