Spring Boot核心技術

ColorPaper發表於2020-11-29

今天來學習spring boot,首先看一下課程體系.本文先負責前8章.前8章主要是學習spring boot的使用和內部的原理,後8章來學習它和一些高階場景的整合.
在這裡插入圖片描述
在這裡插入圖片描述

1. spring boot簡介

簡化spring應用開發的一個框架
對spring整個技術棧的一個整合
java ee開發的一站式解決方案

2. 微服務

微服務:它是一種架構風格,一個應用應該是一組小型服務,可以通過http的方式進行互通,
單體應用:all in one 所有的東西都放在一個應用程式內
優點:

開發測試簡單
部署的話只需要把整個應用達成war包部署到伺服器上即可
當系統負載過大時,直接可以把相同的應用進行水平復制擴充套件

缺點:

這種模式牽一髮動全身,只要對專案進行修改,我們都需要把專案進行重新部署
這種模式無法滿足日益複雜的業務需求

在這裡插入圖片描述
由此,誕生了微服務.

我們把每一個功能元素都獨立放進一個獨立的服務當中,然後根據需求將這些服務進行動態的組合
相比單體應用,我們只是對功能元素的複製,而沒有進行整個應用的複製.
每一個服務都是可以替換,升級的軟體單元.

在這裡插入圖片描述
環境約束

–jdk1.8:Spring Boot 推薦jdk1.7及以上;java version “1.8.0_112”,java -version;進行檢視
–maven3.x:maven 3.3以上版本;Apache Maven 3.3.9, mvn -v : 檢視版本號
–IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64、STS
–SpringBoot 1.5.9.RELEASE:1.5.9;
統一環境;

1、MAVEN設定;

給maven 的settings.xml配置檔案的profiles標籤新增

<profile>
  <id>jdk-1.8</id>
  <activation>
    <activeByDefault>true</activeByDefault>
    <jdk>1.8</jdk>
  </activation>
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
  </properties>
</profile>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4、Spring Boot HelloWorld

瀏覽器傳送hello請求,伺服器接受請求並處理,響應Hello World字串;

1.建立一個maven工程;(jar)

2. 匯入spring boot相關的依賴

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. 編寫一個主程式;啟動Spring Boot應用

/**
 *  @SpringBootApplication 來標註一個主程式類,說明這是一個Spring Boot應用
 */
@SpringBootApplication
public class HelloWorldMainApplication {

    public static void main(String[] args) {

        // Spring應用啟動起來
        SpringApplication.run(HelloWorldMainApplication.class,args);
    }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

編寫相關的controller和service

@Controller
public class HelloController {

    @ResponseBody
    @RequestMapping("/hello")
    public String hello(){
        return "Hello World!";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

5.執行主程式進行測試

6.簡化部署

 <!-- 這個外掛,可以將應用打包成一個可執行的jar包;-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

對於打包完成的專案生成的jar包,我們可以使用java -jar jar包 :在cmd下進行啟動,同樣可以在瀏覽器訪問,英文springboot在打包的時候,都會給當前專案內Tomcat伺服器.

helloword探究

1. pom檔案

1.專案中匯入了一個父專案

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
 </parent>
    
它的父專案還依賴一個父專案
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-dependencies</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath>../../spring-boot-dependencies</relativePath>	     
</parent>
這個父工程中的properties屬性,它定義了每一個依賴的版本號,這個父工程其實真正管理springboot工程中的所有的依賴的版本.
	<properties>
		<!-- Dependency versions -->
		<activemq.version>5.14.5</activemq.version>
		<antlr2.version>2.7.7</antlr2.version>
		<appengine-sdk.version>1.9.59</appengine-sdk.version>
		......
	<properties>	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

這個父工程就是spring boot的版本仲裁中心,我們以後在匯入jar包時,預設是不用寫版本的,(但是沒有在dependencies裡面管理的依賴,我們還是需要宣告版本號的.)

2. 啟動器

	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
	</dependency>
  • 1
  • 2
  • 3
  • 4

專案中只匯入了>spring-boot-starter-web.
spring-boot-stater:spring-boot場景啟動器,幫我們匯入了web模組正常執行所依賴的元件

springboot將所有的功能場景都抽取出來,做成一個個的starters(啟動器),我們只需要在醒目中匯入這些starter,相關場景的所有依賴都會被匯入.而且版本由springboot的版本仲裁中心進行統一管理.要用什麼場景就匯入相應的場景啟動器即可.

2.主程式類,主入口類

@SpringBootApplication
public class HelloworldApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class,args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

@SpringBootApplication:spring boot 應用標註在某個類上,說明是這個類是springboot的主配置類.會執行這個類的main方法來起訂spring boot應用.
我們來深入一下這個@SpringBootApplication註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

@SpringBootConfiguration

@SpringBootConfiguration:spring boot的配置類,
它標註在某一個類上,表示這是springboot的一個配置類,點進去發現,它上邊
標註著 >@Configuration:配置類上標註這個註解.
配置類的作用其實和我們在spring的時候寫的xml的配置檔案差不多,太多的配置檔案太麻煩了,所以我們用配置類替換了.
繼續點選會發現,@Configuration註解上面標註了@Component,這說明配置類也是容器中的元件

@EnableAutoConfiguration

開啟自動配置功能,以前我們需要進行配置的內容,springboot會幫助我們自動配置,@EnableAutoConfiguration告訴springboot開啟自動配置功能.這樣自動配置才能生效

自動配置的原理??

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  • 1
  • 2
  • 3

@AutoConfigurationPackage:
它的作用就是將我們的主配置類(也就是@SpringBootApplication所標註的類,)所在的包及其所有的子包下的所有的元件掃描到spring容器中

自動配置包,他上面標註了 
@Import(AutoConfigurationPackages.Registrar.class)
它使用了spring的底層的 @Import註解,它的作用就是給容器中匯入元件,匯入的元件由括號裡面的內容決定,
本例中匯入的就是AutoConfigurationPackages.Registrar.class這個元件
我們可以在@Import的括號中寫邏輯判斷語句並且返回,匯入元件

深入Registrar這個類

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
//metadata,它值得是註解標註的一些源資訊
@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最重要的就是 new PackageImport(metadata).getPackageName(),我們可以進行一個計算,如下圖:
通過計算,這個方法的返回值真好就是我們這個主配置類所在的包名
所以說,我們這個@AutoConfigurationPackage註解的主要作用就是,對我們主配置類所在的包及其下面的所有的子包下的所有元件進行掃描到spring容器中

在這裡插入圖片描述
@Import(EnableAutoConfigurationImportSelector.class)

給容器中匯入元件
EnableAutoConfigurationImportSelector:開啟自動配置類的導包的選擇器
它會把所有需要的元件以全類名的方式進行返回,然後新增到容器當中.
它最終會給容器中新增非常多的自動配置類(xxxAutoconfiguration),這些自動配置類會給容器中匯入這些場景所需要的所有元件,並且配置好這些元件
在這裡插入圖片描述

有了自動配置類,就免去了我們手動編寫配置,注入功能元件等工作.

那麼容器是如何掃描到這麼多了自動配置類呢?它是從哪得到的呢?

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
  • 1

META-INF/spring.factories下獲取EnableAutoConfiguration指定的值

在這裡插入圖片描述

從上圖中可以看出,
Spring Boot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類匯入到容器中,自動配置類就生效,幫我們進行自動配置工作;
以前我們需要自己配置的東西,自動配置類都幫我們;
J2EE的整體整合解決方案和自動配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar;

6.使用Spring Initializer快速建立spring boot應用.

IDE都支援使用Spring的專案建立嚮導快速建立一個Spring Boot專案;
選擇我們需要的模組;嚮導會聯網建立Spring Boot專案;
>@ResponseBody 註解載入類上,表示這個類的所有方法返回的資料直接寫給瀏覽器(如果是物件轉換為json)
@RestController = @Controller + @ResponseBody

預設生成的Spring Boot專案;

  • 主程式已經生成好了,我們只需要我們自己的邏輯
  • resources資料夾中目錄結構
    • static:儲存所有的靜態資源; js css images;
    • templates:儲存所有的模板頁面;(Spring Boot預設jar包使用嵌入式的Tomcat,預設不支援JSP頁面);可以使用模板引擎(freemarker、thymeleaf);
    • application.properties:Spring Boot應用的配置檔案;可以修改一些預設設定;

二 , Spring Boot 配置

1. 配置檔案

spring boot預設會使用一個全域性配置檔案,(配置檔案的名稱是固定的,不能寫錯)

  • application.properties
  • application.yml

配置檔案的作用就是用來修改springboot 自動配置的預設值
配置檔案的作用:修改SpringBoot自動配置的預設值;SpringBoot在底層都給我們自動配置好;

YAML(YAML Ain’t Markup Language)
YAML A Markup Language:是一個標記語言
​ YAML isn’t Markup Language:不是一個標記語言;
標記語言:
​ 以前的配置檔案;大多都使用的是 xxxx.xml檔案;
​ YAML:以資料為中心,比json、xml等更適合做配置檔案;
​ YAML:配置例子

server:
  port: 8088
  • 1
  • 2
<server>
	<port>8088</port>
</server>
  • 1
  • 2
  • 3

對比可以發現,xml的配置檔案大量的資料都浪費在了標籤的開闢上,而yml卻非常的簡介,所以說它是以資料為中心

2.YAML的語法

在這裡插入圖片描述
在yml的世界中,無論你的資料時哪種型別,k和v之間是一定要有一個空格的

它使用空格來控制層級關係,只要是左對齊的一列資料,都屬於同一層級
屬性和值都是大小寫敏感的,嚴格區分大小寫

2. yaml值的寫法

字面量:普通型別的值(數字,字串,布林等)

k: v:字面直接來寫;
​ 字串預設不用加上單引號或者雙引號;
​ “”:雙引號;不會轉義字串裡面的特殊字元;特殊字元會作為本身想表示的意思
​ ‘’:單引號;會轉義特殊字元,特殊字元最終只是作為一個普通的字串資料被解讀

物件,map(屬性和值) (鍵值對)

物件和map一樣,都是使用鍵值對來表示.物件有兩種表示形式.
1.第一種使用空格控制屬性和值.

student:
	name: zhangsan
	age: 18
  • 1
  • 2
  • 3

2.屬性和值都寫在同一行的大括號內,多個屬性值之間使用逗號分隔

student: {name: zhangsan,age: 18}
  • 1

陣列(list,set):

陣列也是有兩種寫法:
1,使用空格 -,來表示一個元素,注意使用空格控制層級關係

pets:
	- dog
	- cat
	- pig
  • 1
  • 2
  • 3
  • 4

2.行內寫法(使用中括號表示整個陣列,每個元素有逗號分隔)

pets: [dog,cat,pig]
  • 1

3.配置檔案值注入

第一種 :從配置檔案中進行屬性值的注入
我們可以匯入一個配置檔案處理器,這樣在編寫配置檔案就有提示的功能

   <!--匯入配置檔案處理器,配置檔案進行繫結就會有提示-->
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-configuration-processor</artifactId>
         <optional>true</optional>
    </dependency> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

總結:

將配置檔案中的值,對映到這個元件中,
我們需要使用一個註解就是
@ConfigurationProperties:告訴springboot,本類中的所有屬性需要和配置檔案中
的相關配置進行繫結,它預設是從全域性配置檔案中獲取值
prefix = “persion”,讀取配置檔案中的哪個字首下的值和屬性進行一一對映
@ConfigurationProperties必須和@Component一起使用才會生效,因為
只有容器中的元件才能使用容器提供的@ConfigurationProperties功能

/**
 * 將配置檔案中的值,對映到這個元件中,
 * 我們需要使用一個註解就是
 * @ConfigurationProperties:告訴springboot,本類中的所有屬性需要和配置檔案中
 * 的相關配置進行繫結
 * prefix = "persion",讀取配置檔案中的哪個字首下的值和屬性進行一一對映
 * @ConfigurationProperties必須和@Component一起使用才會生效,因為
 * 只有容器中的元件才能使用容器提供的@ConfigurationProperties功能
 */
@Component
@ConfigurationProperties(prefix = "persion")
public class Persion {
    private String lastname;
    private int age;
    private boolean boss;
    private Date birth;

    private Map<String,Object> maps;
    private List<Object> lists;

    private Dog dog;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

注意

idea預設是用的是utf-8編碼,.properties預設使用的是ASCII編碼,
如果我們從application.properties檔案中注入屬性,很可能出現中文亂碼
修改方案如下圖
在這裡插入圖片描述

第二種:我們使用 @Value註解來進行屬性值注入

 <bean class="Person">
     <property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property>
   <bean/>

  • 1
  • 2
  • 3
  • 4

其實

@Component註解就類似於xml中的標籤,都是往容器中新增元件
@Value相當於,對元件的屬性進行值注入
它支援寫入的資料方式:
字面量,@Value(“字面量”)
$ {} :從環境變數,配置檔案中獲取值. @Value("${}")
#{spEL} :支援spring的EL表示式 @Value("#{}")

@Value("${persion.lastname}")
    private String lastname;

    @Value("#{11*2}")
    private int age;

    @Value("true")
    private boolean boss;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

@value獲取值和@ConfigurationProperties獲取值進行比較

 @ConfigurationProperties@Value
功能批量注入配置檔案中的屬性(只需要指定一個perfix,)一個個指定
鬆散繫結(鬆散語法)支援不支援
SpEL不支援支援
JSR303資料校驗支援(需要結合 @Validated使用)不支援
複雜型別封裝支援不支援

那麼我們到底如何選取二者為值注入?

如果說,我們只是在某個業務邏輯中需要獲取一下配置檔案中的某項值,使用@Value;
如果說,我們專門編寫了一個javaBean來和配置檔案進行對映,我們就直接使用@ConfigurationProperties;
多個值注入選用@ConfigurationProperties比較方便

鬆散繫結語法:

無論你寫的是firstName,first-name,first_name,都可以對firstname屬性進行值注入

在這裡插入圖片描述

3、配置檔案注入值資料校驗

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {

    /**
     * <bean class="Person">
     *      <property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property>
     * <bean/>
     */

   //lastName必須是郵箱格式
    @Email
    private String lastName;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.@PropertySource && @ImportResource

@PropertySource : 載入指定的配置檔案,(value的值其實是一個陣列,也就是可以載入多個配置檔案)

@PropertySource(value = {"classpath:persion.properties"})
@Component
@ConfigurationProperties(prefix = "persion")
public class Persion {
  • 1
  • 2
  • 3
  • 4

@ImportResource:匯入spring的配置檔案,讓其內容生效
spring boot中是沒有spring的配置檔案的,我們自己編寫的配置檔案也是不能被自動識別的.如果想讓配置檔案生效,載入到容器,我們需要把 @ImportResource標註在一個配置類上.
@ImportResource(locations = {“classpath:beans.xml”})
locations也是一個陣列,可以載入多個配置檔案的

剛才這種方式往容器中新增元件,十分麻煩,我們需要寫一個配置檔案,然後使用@ImportResource註解,springboot推薦的是下面這種方式

全註解的方式新增元件

1.編寫一個配置類(它的功能就是替代我們之前寫的spring的配置檔案),標註 @Configuration能夠是一個類稱為配置類,
2.配置類中我們是用 @Bean來新增元件,(@Bean標註在方法上時,表示將該方法的返回值新增到容器中,被新增的這個元件的id就是該方法的方法名)

@Configuration
public class MyAppConfig {

    //@Bean標註在方法上,表示的是將方法的返回值新增到容器中
    //容器中這個元件的id就是該方法的方法名
    @Bean
    public HelloService helloService(){
        System.out.println("@Bean 給容器中新增元件了!");
        return new HelloService();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.配置檔案佔位符

1.隨機數

${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
  • 1
  • 2

2、佔位符獲取之前配置的值,如果沒有可以用:指定預設值

person.last-name=張三${random.uuid}
person.age=${random.int}
person.dog.name=${person.hello:hello}_dog
  • 1
  • 2
  • 3

5. Profile

springboot提供了多環境的選擇,為了滿足開發,測試,生產等不同的環境,我們來說兩種方式
在這裡插入圖片描述

1.多個Profile檔案

我們為不同的環境編寫多個Profile檔案,檔案的命名就是 application-{profile標識}.properties
預設使用的application.properties檔案
我們可以全域性配置檔案(也就是application.properties)中通過 spring.profiles.active= profile標識來切換不同的環境

2.yml支援多文件塊方式

yml可以通過 — 來劃分一個文件塊.當我們指定的文件塊被啟用時,它的配置猜會生效

server:
  port: 8081
spring:
  profiles:
    active: dev
---
server:
  port: 8082

spring:
  profiles: dev


---
server:
  port: 8083
spring:
  profiles: prod
---
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.啟用指定Profile

1.在配置檔案中指定 spring.profiles.active=dev
2.通過命令列: (命令列的優先順序高於配置檔案的)
–spring.profiles.active=dev,這個命令修改 Program arguments的值,這個是在測試的時候,不用打包
打過包之後,也可以 : java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
3.修改虛擬機器引數:
-Dspring.profiles.active=dev,這個是固定寫法.
在這裡插入圖片描述

6.配置檔案的載入位置

在這裡插入圖片描述
springboot 啟動會掃描以下位置的application.properties或者application.yml檔案作為Spring boot的預設配置檔案,下面就是springboot載入配置檔案的位置,優先順序由高到低,逐步降低,

–file:./config/ : 當前專案下的config資料夾下
–file:./ :當前專案下
–classpath:/config/ : 類路徑(resource資料夾)下的config資料夾下
–classpath:/ 類路徑下

上面就是springboot載入配置檔案的位置,優先順序由高到低,逐步降低,

相同配置內容同時存在時:高優先順序別的配置檔案會對低優先順序的配置進行覆蓋
而不同的配置則不會進行覆蓋,並不是高優先順序的載入之後就不載入低優先順序的
SpringBoot會從這四個位置全部載入主配置檔案;形成了 互補配置

我們可以通過== spring.config.location 來改變預設的配置檔案的位置==

我們直接在配置檔案中這樣寫是不會生效的
該命令一般都是專案上線之後,在運維時使用的.我們使用命令列的方式執行
我們只需要重新寫一份配置檔案,只需要編寫少量的配置,就可以和之前的配置共同起作用.特別適合運維.不需要更改整個配置然後打包釋出

java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring
.config.location=D:/application.properties
  • 1
  • 2

7.外部配置的載入順序

我們對springboot的配置不僅僅是可以寫在application.properties檔案內,還可以從以下位置中載入配置.優先順序從高到低,所有配置都支援的是互補配置.(總共是11個,官網是17個,我們只關心,1,6,7,8,9)

1.命令列引數
所有的配置都可以在命令列上進行指定
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc 多個配置用空格分開; --配置項=值
2.來自java:comp/env的JNDI屬性
3.Java系統屬性(System.getProperties())
4.作業系統環境變數
5.RandomValuePropertySource配置的random.*屬性值

由jar包外向jar包內進行尋找;
優先載入帶profile的
6.jar包外部的application-{profile}.properties或application.yml(帶spring.profile)配置檔案
7.jar包內部的application-{profile}.properties或application.yml(帶spring.profile)配置檔案
再來載入不帶profile的
8.jar包外部的application.properties或application.yml(不帶spring.profile)配置檔案
9.jar包內部的application.properties或application.yml(不帶spring.profile)配置檔案

10.@Configuration註解類上的@PropertySource
11.通過SpringApplication.setDefaultProperties指定的預設屬性

8.自動配置原理

springboot的全域性配置檔案,到底如何寫?能寫哪些內容?
官方的屬性參照
自動配置原理
它是springboot的精髓所在,理解了它就能隨心所欲的進行配置.

1.springboot 在啟動的時候,載入主配置類,並且通過主配置類上標註的 @EnableAutoConfiguration開啟了自動配置功能,
2,@EnableAutoConfiguration作用:

  • 利用 @Import(AutoConfigurationImportSelector.class)給容器中匯入了一些元件,跟蹤原始碼,會發現,
  • public String[] selectImports(AnnotationMetadata annotationMetadata) {}方法中的
  • List < String> configurations =getCandidateConfigurations(annotationMetadata,attributes); //它的作用就是獲取候選的配置
    • SpringFactoriesLoader.loadFactoryNames()
      //它會掃描所有jar包類路徑下 META-INF/spring.factories
	Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  • 1

//把掃描到的這些檔案的內容包裝成properties物件

  •   for (Map.Entry<?, ?> entry : properties.entrySet()) {
      String factoryClassName = ((String) entry.getKey()).trim();
      for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
      result.add(factoryClassName, factoryName.trim());
      } ```
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • //通過對properties 檔案的遍歷,從properties檔案彙總獲取到EnableAutoConfiguration.class類(類名)對應的值,然後把他們新增在容器中

3.每一個 自動配置類都會進行自動配置.

總結:
springboot在啟動時,會將 類路徑下 META-INF/spring.factories 裡面配置的所有EnableAutoConfiguration的值加入到了容器中

下面只是列舉了其中的一小部分
這裡面的每一個XXXAutoConfiguration都是容器中的一個元件,都會加入到容器中紅,他們的作用就是 用來做自動配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.舉例 HttpEncodingAutoConfiguration解釋自動配置原理
@Configuration:表示這是一個配置類,作用和之前編寫的spring的配置檔案類似,也可以向容器中新增元件
@EnableConfigurationProperties(HttpProperties.class) :開啟指定類的ConfigurationProperties功能,然後把這個類加入到容器中.

@Configuration
//表示這個類是一個配置類

@EnableConfigurationProperties(HttpProperties.class)
//@EnableConfigurationProperties(HttpProperties.class) :開啟指定類的ConfigurationProperties功能,然後把這個類加入到IOC容器中

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) 
//@ConditionalOn底層使用的是spring的@Conditional註解,根據我們的條件進行判斷,如果滿足我們的條件,整個配置類裡面的配置才會生效,判斷這是不是一個web應用,如果是,當前配置類生效

@ConditionalOnClass(CharacterEncodingFilter.class)
//判斷當前專案中有沒有CharacterEncodingFilter這個類,這個類是springmvc中用來解決亂碼的過濾器

@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
		matchIfMissing = true)
//判斷配置hi檔案中是否含有某個配置,  spring.http.encoding.enabled; 
//matchIfMissing = true表示的是如果不存在這個配置,也是預設生效的

public class HttpEncodingAutoConfiguration {

	//他已經和SpringBoot的配置檔案對映了
  	private final HttpEncodingProperties properties;
	
	//只有一個有參構造器的情況下,引數的值需要從容器中獲取
	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}

	@Bean //給容器中新增一個元件,該元件中某些值需要從properties中獲取
	//容器中必須不包含某個類
	@ConditionalOnMissingBean 
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  1. @ConfigurationProperties(prefix = “spring.http”):表示從配置檔案中獲取指定的在值來和本類中的屬性進行繫結,獲取值的字首時spring.http.將配置檔案中對應的值和HttpEncodingProperties繫結起來
    2.這也就是告訴我們,配置檔案到底如何寫,我們可以在全域性配置檔案(application.properties/yml)中,編寫spring.http.x=y;例如:
    spring.http.encoding.charset=UTF-8
    spring.http.encoding.force=true
    3.具體需要配置的x可以在propertis類中檢視
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
  • 1
  • 2

5.所有在配置檔案中能配置的屬性都是在xxxxProperties類中封裝者‘;配置檔案能配置什麼就可以參照某個功能對應的這個屬性類

根據不同的條件進行判斷,配置類是否生效 ?

1.一但這個配置類生效;這個配置類就會給容器中新增各種元件;這些元件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的;
2.我們到底能在配置檔案中配置哪些內容,都是由xxxProperties類中的屬性決定的

精髓:

​ 1)、SpringBoot啟動會載入大量的自動配置類
​ 2)、我們看我們需要的功能有沒有SpringBoot預設寫好的自動配置類;
​ 3)、判斷這個自動配置類到底會不會生效,如果生效,我們再來看這個自動配置類中到底配置了哪些元件;(只要我們要用的元件有,我們就不需要再來配置了)
​ 4)、給容器中自動配置類新增元件的時候,會從properties類中獲取某些屬性。我們就可以在配置檔案中指定這些屬性的值;

xxxxAutoConfigurartion:自動配置類;給容器中新增元件,元件的某些屬性值會從和xxxxProperties對映的配置檔案中讀取
xxxxProperties:封裝配置檔案中相關屬性;

2. 細節

1.@Conditional派生註解(Spring註解版原生的@Conditional作用)

作用:就是必須要使得@Conditional註解指定的條件成立,配置類內的配置才會生效,才會給容器中新增元件

@Conditional擴充套件註解作用(判斷是否滿足當前指定條件)
@ConditionalOnJava系統的java版本是否符合要求
@ConditionalOnBean容器中存在指定Bean;
@ConditionalOnMissingBean容器中不存在指定Bean;
@ConditionalOnExpression滿足SpEL表示式指定
@ConditionalOnClass系統中有指定的類
@ConditionalOnMissingClass系統中沒有指定的類
@ConditionalOnSingleCandidate容器中只有一個指定的Bean,或者這個Bean是首選Bean
@ConditionalOnProperty系統中指定的屬性是否有指定的值
@ConditionalOnResource類路徑下是否存在指定資原始檔
@ConditionalOnWebApplication當前是web環境
@ConditionalOnNotWebApplication當前不是web環境
@ConditionalOnJndiJNDI存在指定項

雖然springboot在啟動時載入了很多自動配置類(預設好像是載入了96個),但是不是所有的都生效,自動配置類需要滿足一定的條件才能生效,那麼我們怎麼知道到底哪些生效了,哪些沒有生效呢?

1.如果不怕麻煩,你可以開啟類路徑下 /META-INF/spring.factories,找到,EnableAutoConfiguration,然後一個個的檢視,到底哪個生效了.
2.debug=true,開啟debug模式,在application,properties中寫上這一句.啟動應用就會在控制檯進行輸出,我們可以很方便的看出到底誰生效了,誰沒有是生效.

在這裡插入圖片描述
在這裡插入圖片描述

三. springboot與日誌

1.日誌框架

市面上的日誌框架;
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…

日誌門面 (日誌的抽象層)日誌實現
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-loggingLog4j JUL(java.util.logging) Log4j2 Logback

左邊選一個門面(日誌的抽象層,類似JDBC)、右邊來選一個實現;
日誌門面: SLF4J;
日誌實現:Logback;
slf4j和log4j和logback都是同一個人寫的,但是log4j有效能問題,所有我們使用logback,log4j2其實是Apache的產品,只不過是借用了人家的名字.

springboot底層是spring,spring的日誌框架是 jcl ,但是springboot的底層選用的是slf4j和logback

2. slf4j的使用

1.如何在系統使用slf4j

在開發的時候,日誌方法的呼叫,不應該是直接呼叫日誌的實現類,而是呼叫日誌抽象層裡面的方法
我們首先要給系統中匯入slf4j的jar和logback實現的jar

示例程式碼:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
  //呼叫的的是抽象層LoggerFactory的getLogger方法
  //getLogger(),獲取記錄器,記錄器會記錄指定類的資訊
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    //我們記錄的資訊可以列印到控制檯,或者是輸出到日誌檔案中
    logger.info("Hello World");
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

如果想要用其他的日誌實現,不用預設的logback呢?
先來看官方給的一個圖:
在這裡插入圖片描述
解釋上圖:

上圖給我們解釋了該如何具體的使用slf4j,以及該匯入那些包

  1. 使用logback: 匯入slf4j-api.jar ,logback-classic.jar, logback-core.jar
  2. 使用log4j: 匯入slf4j-api.jar ,log4j.jar,和適配層的 slf4j-log412.jar
  3. 使用JUL: 匯入slf4j-api.jar,jul的jar.和適配層的slf4j-jdk14.jar

為設麼要匯入適配層的jar?

因為啊,這個log4j和JUL的出現比slf4j要早很多,人家的產品壓根就沒有考慮會出現這個抽象層的slf4j,
slf4j為了整合log4j和jul,推出了相應的適配層的jar,適配層包含實現了slf4j 的具體方法,還會呼叫log4j或者jul的api.
流程 : 應用 >>> 呼叫slf4j的api >>> 呼叫適配層的方法 >>> 呼叫log4j/jul 的api

每一個日誌框架都有自己的配置檔案,使用了sf4j之後,日誌檔案還是做成日誌實現框架本身的配置檔案,也就是說,我們的slf4j只是提供 了一個統一抽象層

如果使用的是logback,那麼還是寫logback的配置檔案
如果使用的是log4j,那麼就寫log4j的配置 檔案

2. 遺留問題

我們在實際的開發過程中,可能使用各種框架,他們都有自己的日誌實現 ,例如,springboot( slf4j + logback ) ,hibernate (jbss-logging) , spring(jcl),…這個看上去進很亂.我們需要統一日誌記錄

統一日誌記錄

即使使用到別的框架,那麼也要使用slf4j+logback,進行日誌輸出.

在這裡插入圖片描述
如何統一使用slf4j進行日誌記錄?

1. 首先要把其他日誌框架進行排除
2. 用中間包替換掉原有的日誌框架
3. 我們匯入slf4j的其他的實現.

對上面的步驟進行解釋:

1.為了統一使用slf4j進行日誌記錄,我們首先要把其他的日誌框架進行排除,例如spring要排除jcl,hibernate 要排除jboss,還有要排除log4j,jul等
2.排除其他的日誌框架之後,我們的其他框架肯定會報錯,這時我們需要一個替換包來代替那些被替換掉的日誌框架,替換包的功能就是替換其他的日誌框架,然後還能支援抽象層的slf4j,例如 jcl-over-slf4j.jar, log4j-over-slf4j.jar, jul-to-slf4j.jar,分別是用來替換jcl,log4j,jul的
替換完成之後,我們 就可以使用你心儀的日誌框架,如果你想用log4j,你需要匯入適配層slf4j-log412.jar, 想用jul需要匯入適配層 slf4j-jdk14.jar.
這樣,我們只需要呼叫 抽象層slf4j的api, 抽象層會通過適配層呼叫具體的日誌實現,這樣我們就實現了日誌的統一記錄.

3.springboot的日誌關係

每一個springboot應用找那個都包含spring-boot-start這兒依賴

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
  • 1
  • 2
  • 3
  • 4

spring boot 使用它來做日誌工程:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.1.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

底層依賴關係:在這裡插入圖片描述
總結:

1 SpringBoot底層也是使用slf4j+logback的方式進行日誌記錄
2 SpringBoot通過中間替換包也把其他的日誌都替換成了slf4j;

在這裡插入圖片描述

4 從上圖中,我們可以看出,springboot統一日誌記錄採用的就是slf4j官網上圖片上的步驟,它在底層已經為我們完成了替換,我們如果引入了其他的框架,需要日誌的話,一定要把引入的框架預設的日誌依賴給移除掉.因為替換包中的日誌的包名,類名字和預設日誌框架中的包名,類名全部一樣.如果不移除,jar吧就會發生衝突,肯定要報錯的

springboot引入其他的框架時,在底層首先排除了人家預設的日誌框架,例如spring使用的是commons-logging;springbot 就把commons-logging給移除了

<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

日誌核心總結:

springboot能夠自動適配所有日誌框架,並且在底層採用的都是slf4j+logback的方式記錄日誌.我們在引入其他的框架的時候,只需要把該該框架預設的日誌框架給排除掉就行了

4.日誌的使用

1 預設配置

spring boot預設已經幫我們做好了日誌的配置,可以直接使用,示例如下:

public class SpringBoot03LoggingApplicationTests {

    //記錄器
    Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void contextLoads() {
        //trace翻譯就是跟蹤軌跡的意思,它是最低階別的,但也是列印資訊最詳細的
        //日誌的級別: trace debug info warn error,級別是由低到高,預設級別是info
        //我們可以調整日誌級別來控制日誌資訊的輸出,它就會列印本級別和更高階別的日誌資訊
        logger.trace("這個是trace日誌.....");
        logger.debug("這個是debug日誌.....");
        //沒有指定級別的,就使用springboot預設的root級別
        logger.info("這個是info日誌.....");
        logger.warn("這個是warn日誌.....");
        logger.error("這個是error日誌.....");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

//trace翻譯就是跟蹤軌跡的意思,它是最低階別的,但也是列印資訊最詳細的
//日誌的級別: trace debug info warn error,級別是由低到高,預設級別是info
//我們可以調整日誌級別來控制日誌資訊的輸出,它就會列印本級別和更高階別的日誌資訊
//沒有指定級別的,就使用springboot預設的root級別

可以在application.properties檔案中配置日誌:

# 我們可以調整每一個包的日誌級別
logging.level.com.atguigu=trace

# logging.path主要是指定,日誌檔案的目錄,/表示的是當前磁碟的根目錄
# 它會在當前磁碟根目錄下建立spring/log資料夾,在該檔案下存放日誌檔案,
# 檔案的預設名是 spring.log

logging.path=/spring/log

# 不指定路徑的話,它會在當前專案下生成spring-boot.log日誌檔案,也可以在檔案前加上路徑
# 指明配置檔案的生成位置
logging.file=D:/spring-boot.log

# 指定在控制檯輸出的日誌的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n

# 指明日誌檔案中的輸出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

logging.file和loggingpath的區別

logging.filelogging.pathExampleDescription
(none)(none) 只在控制檯輸出
指定檔名(none)my.log輸出日誌到my.log檔案
(none)指定目錄/var/log輸出到指定目錄的 spring.log 檔案中

日誌資訊的語法如下:

日誌輸出格式:
		%d表示日期時間,
		%thread表示執行緒名,
		%-5level:級別從左顯示5個字元寬度
		%logger{50} 表示logger名字最長50個字元,否則按照句點分割。 
		%msg:日誌訊息,
		%n是換行符
    -->
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.指定配置

我們只能在application.yml配置檔案修改一些特別日誌的配置,

我們在application.properties中編寫的日誌配置,其實都會被儲存在LoggingApplicationListener這個類中,而這個listener中配置的資訊,最終會被封裝到屬性類LoggingSystemProperties中,它裡面的apply方法會對包裝過的配置進行解析.

閱讀原始碼我們會發現

springboot已經配置好了日誌,包括預設的日誌檔案是以追加的方式,預設大小10M,超過了10M,檔名會自增繼續建立.

但是我們感覺這些還不夠用,我們想要使用logback的一些高階功能?
例如要 非同步日誌 ,自動歸檔了等功能,這是我們就需要自己來寫日誌的配置檔案

Logging SystemCustomization
Logbacklogback-spring.xmllogback-spring.groovylogback.xml or logback.groovy
Log4j2log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging)logging.properties

如果你想用自己的日誌配置檔案,那麼 給類路徑下放上你要用的日誌框架自己的配置檔案即可;SpringBoot就不使用他預設配置的了,配置檔案的命名如上表所示

推薦使用 logback-spring.xml組委自定義的日誌配置檔案

1.logback.xml : 它會繞過springboot被日誌框架直接額載入
2.logback-spring.xml : 它不會被日誌框架直接載入,而是由springboot載入,這樣做的優點就是,我們可以在配置檔案內使用< springProfile>標籤,這個標籤可以 把某段配置只在特定的啟用環境中生效
3.如果你檔名是 logback.xml,但是你在檔案中使用了< springProfile>標籤,那麼因公啟動就會報錯no applicable action for [springProfile]

<springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
  	可以指定某段配置只在某個環境下生效
</springProfile>
  • 1
  • 2
  • 3
  • 4

5 .切換日誌框架

spring-boot-starter-logging和spring-boot-starter-log4j2,都是用來做日誌的,springboot預設的是logging ,我們也可以切換為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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

原理就是 把原來的logging排除,然後新增log4j2

spring-boot-starter-logging的底層使用的是 slf4j + logback的方式,我們也可以把logback替換掉,當然這樣做是沒有意義的,在這裡只是學習一下原理:

1.把logback的依賴給排除
2.把之前logback 替換log4j的依賴給排除
3.新增log4j 的依賴
4.新增slf4j為log4j做的適配包
5.你同樣可以為你的新的日誌框架寫一個自定義的配置檔案,放在類路徑下,名為為 xxx-spring.xml,推薦加上-spring,這樣可以使用高階功能.

日誌配置檔案中配置非同步日誌

<appendername="ASYNC_ROLLING_FILE"class="ch.qos.logback.classic.AsyncAppender">
<appender-refref="ROLLING_FILE"/>
</appender>
<rootlevel="INFO">
<appender-refref="ASYNC_ROLLING_FILE"/>
</root>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

四 ,spring boot與web開發

1.使用SpringBoot;

1)、建立SpringBoot應用,選中我們需要的模組;
2)、SpringBoot已經預設將這些場景配置好了,只需要在配置檔案中指定少量配置就可以執行起來
3)、自己編寫業務程式碼;

自動配置原理?

這個場景SpringBoot幫我們配置了什麼?能不能修改?能修改哪些配置?能不能擴充套件?xxx

1.xxxxAutoConfiguration:幫我們給容器中自動配置元件;
2.xxxxProperties:配置類來封裝配置檔案的內容;它通過@ConfigurationProperties(prefix = “”)來完成配置類的屬性和配置檔案中之間的對映
他們兩者之間通過@EnableConfigurationPropertie(),進行連線

2. spring boot對靜態資源的對映規則

可以在配置檔案中通過spring.resources設值和靜態資源相關的引數,快取時間等.

@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
  • 1
  • 2

springboot中對SpringMvc的所有配置都在 WebMvcAutoConfiguration這個類中,這個類中有個方法,作用是 addResourceHandlers 新增資源對映,程式碼如下:


		@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();
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(
						registry.addResourceHandler(staticPathPattern)
								.addResourceLocations(getResourceLocations(
										this.resourceProperties.getStaticLocations()))
								.setCachePeriod(getSeconds(cachePeriod))
								.setCacheControl(cacheControl));
			}
		}
		
		//配置歡迎頁的對映
		@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(
				ApplicationContext applicationContext) {
			return new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext),
					applicationContext, getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
		}

		//配置我們自己喜歡的圖示
		@Configuration
		@ConditionalOnProperty(value = "spring.mvc.favicon.enabled",
				matchIfMissing = true)
		public static class FaviconConfiguration implements ResourceLoaderAware {

			private final ResourceProperties resourceProperties;

			private ResourceLoader resourceLoader;

			public FaviconConfiguration(ResourceProperties resourceProperties) {
				this.resourceProperties = resourceProperties;
			}

			@Override
			public void setResourceLoader(ResourceLoader resourceLoader) {
				this.resourceLoader = resourceLoader;
			}

			@Bean
			public SimpleUrlHandlerMapping faviconHandlerMapping() {
				SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
				mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
				mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
						faviconRequestHandler()));
				return mapping;
			}

			@Bean
			public ResourceHttpRequestHandler faviconRequestHandler() {
				ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
				requestHandler.setLocations(resolveFaviconLocations());
				return requestHandler;
			}

			private List<Resource> resolveFaviconLocations() {
				String[] staticLocations = getResourceLocations(
						this.resourceProperties.getStaticLocations());
				List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
				Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
						.forEach(locations::add);
				locations.add(new ClassPathResource("/"));
				return Collections.unmodifiableList(locations);
			}

		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

1./webjars/ : 請求:

請求都去classpath:/META-INF/resources/webjars/ ,下找資源,webjars:指的就是以jar包的方式引入資源,它把我們要用的jQuery,bootstrap等整合成了maven,詳情請參考網站
在這裡插入圖片描述
測試案例:http://localhost:8080/webjars/jquery/3.3.1/jquery.js, 在訪問時,我們只需要些要訪問的資源就行

<!--引入jQuery的webjar-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.3.1</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2. “/**” : 訪問當前專案的任何資源(這幾個都是用來存放專案的靜態資源的資料夾,只要是請求沒有處理,都會在以下資料夾下尋找資源)

1. “classpath:/META-INF/resources/”,
2. “classpath:/resources/”,
3. “classpath:/static/”,
4. “classpath:/public/”
5. “/” :當前專案的根路徑
java和resources都是類路徑的根路徑
訪問的規則:localhost:8080/abc : 如果沒人處理,就會去以上幾個檔案下尋找abc

3 . 歡迎頁 : 靜態資原始檔夾下的index.html頁面,會被 ‘’ /** ''對映
訪問localhost:8080/ 它就會找index.html
4.專案圖示,所有的 **/favicon.ico,都會在靜態資原始檔夾下找
5.修改系統預設的靜態資原始檔夾

1.從類ResourcesProperty的源原始碼的標註得知,我們可以在application.properties使用spring.resource來修改和靜態資源,快取等有關的引數
2.spring.resources.static-locations = classpath:/hello,classpath:/aynu,修改的是預設的靜態資原始檔夾,這個locations是一個陣列,也就是可以指定多個靜態資原始檔夾,多個元素之間使用逗號分隔

3.模板引擎

常見的模板引擎 : JSP Freemarker,Thymeleaf,等,他們的原來都是相通的
在這裡插入圖片描述

模板引擎的原理當我們把模板頁面和資料都交給模板引擎時,它會把資料解析然後填充到模板頁面中指定的位置,進行輸出.只不過不同的引擎的語法可能不太一樣

springboot推薦使用的是Thymeleaf,使用方法如下:

1.引入Thymeleaf

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4

預設的版本太低了,我們要使用Thymeleaf3,我們可以在properties標籤中覆蓋要來的預設版本

切換你Thymeleaf版本

<properties>
		<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
		<!-- 佈局功能的支援程式  thymeleaf3主程式  layout2以上版本 -->
		<!-- thymeleaf2   layout1-->
		<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
  </properties>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.thymeleaf的語法和使用

根據自動配置原理,我們可以得知,可以在配置檔案中編寫的引數都儲存在名為xxxProperties 的屬性類中

@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";
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由此得知,我們只要把HTML頁面(也就是目標頁面)放在classpath:/templates/資料夾下,Thymeleaf模板引擎就可以自動幫我們渲染頁面了

使用:
1.匯入Thymeleaf的名稱空間(這樣就會有提示的功能)

<html lang="en" xmlns:th="http://www.thymeleaf.org">
  • 1

2.使用Thymeleaf的語法

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>
    <!--th:text 它可以替換我們div中的文字,相當於模板引擎的資料填充-->
    <div th:text="${hello}">這是我們要顯示的內容</div>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.語法規則

  1. th:text : 改變當前元素的文字內容,我們可以使用 th:任意HTML屬性 來替換原生屬性的值 (屬性值可以如下圖)
    在這裡插入圖片描述

2.能寫哪些表示式?
``
Simple expressions:(表示式語法)
#{} 的語法功能如下:它是我們使用最多功能最強大的表示式

(1).
${}它主要是用來獲取物件的屬性,包括級聯屬性,呼叫方法,還能使用一些內建的基本物件

  • ${person.father.name} : 獲取物件的級聯屬性
  • ${countriesByCode.ES} :從 map中讀取屬性值
  • ${personsByName[‘Stephen Zucchini’].age} : 如果從map獲取元素,map的key值中包含空格,需要按照這樣寫
  • ${personsArray[0].name}:從陣列中獲取屬性,如果想獲取物件的屬性,也可以獲取
  • ${person.createCompleteName()}
    ${person.createCompleteNameWithSeparator(’-’)},我們可以使用 ${}來呼叫物件的方法,以及給方法傳引數

==(2) ${} 可以使用內建的基本物件

  • #ctx: the context object. 當前上下文物件
  • #vars: the context variables. 當前上下文中的變數值
  • #locale : the context locale. 區域資訊
  • #request : (only in Web Contexts) the HttpServletRequest object.
  • #response : (only in Web Contexts) the HttpServletResponse object.
  • #session : (only in Web Contexts) the HttpSession object.
  • #servletContext : (only in Web Contexts) the ServletContext object.
  • 示例程式碼: < span th:text="${#locale.country}">US< /span>
  • 獲取 請求引數中的值
    ${param.foo} // Retrieves a String[] with the values of request parameter ‘foo’
    ${param.size()}
    ${param.isEmpty()}
    ${param.containsKey(‘foo’)}

(3) ${}還可以使用內建的工具物件

  • #execInfo : information about the template being processed.
  • #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
  • #uris : methods for escaping parts of URLs/URIs
  • #conversions : methods for executing the configured conversion service (if any).
  • #dates : methods for java.util.Date objects: formatting, component extraction, etc.
  • #calendars : analogous to #dates , but for java.util.Calendar objects.
  • #numbers : methods for formatting numeric objects.
  • #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
  • #objects : methods for objects in general.
  • #bools : methods for boolean evaluation.
  • #arrays : methods for arrays.
  • #lists : methods for lists.
  • #sets : methods for sets.
  • #maps : methods for maps.
  • #aggregates : methods for creating aggregates on arrays or collections.
  • #ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).

*{}的功能:

*(1) {} 選擇表示式

  • 它和${}的功能是一樣的,但是它還有一個補充功能,他可以配合 th:object來使用,代替當前div中的元素
 div th:object="${session.user}">
<p>Name: < span th:text="*{firstName}">Sebastian< /span>.< /p>
 <p>Surname: < span th:text="*{lastName}">Pepper< /span>.< /p>
<p>Nationality: < span th:text="*{nationality}">Saturn< /span>.< /p>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 上述程式碼中我們就可以在該div中使用*{}來代替${session.user},要配合th:object一起使用.

#{}的功能

它是用來獲取國際化內容的

@{}的功能

他可以幫我們定義URL連線的
我們之前在jsp頁面中的超連結中攜帶引數都是通過 連結?引數名引數值,現在使用Thymeleaf,使用th:href代替a:href,然後 ,所有需要在連結中攜帶的引數以key=value的方式,統一放到小括號內部,多個kv之間使用逗號分隔,其中value也可以使用${}的方式獲取值,並且我們的超連結路徑也可以簡寫,直接使用 / 來代替當前的專案路徑

例如 : 'http://localhost:8080/gtvg/order/details?orderId=3’替換之後寫為
th:href="@{/order/details(orderId=${o.id})}"

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

~{…} : 片段引用

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 ,…
Text operations:(文字操作)
		String concatenation: +
		Literal substitutions: |The name is ${name}|
Arithmetic operations:(數學運算)
		Binary operators: + , - , * , / , %
		Minus sign (unary operator): -
Boolean operations:(布林運算)
		Binary operators: and , or
		Boolean negation (unary operator): ! , not
Comparisons and equality:(比較運算)
		Comparators: > , < , >= , <= ( gt , lt , ge , le )
		Equality operators: == , != ( eq , ne )
Conditional operators:(條件運算子,三元運算子)
		If-then: (if) ? (then)
		If-then-else: (if) ? (then) : (else)
		Default: (value) ?: (defaultvalue)
Special tokens:三元運算如果成立或者不成立不想執行任何操作,寫成_就行
Page 17 of 104No-Operation: _
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1.高階寫法,行內寫法,[[]] ==== th:text(會轉義特殊字元),[()] ===== th:utext (不會轉義特殊字元)
2.th:text,寫在那個標籤上,那麼每次遍歷,都會生成一個該標籤

4. SpringMVC的自動配置

可以參考官網

Spring Boot 自動配置好了SpringMVC

以下是springboot對SpringMVC的預設配置:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
    • 自動配置了ViewResolver(作用:根據我們處理器方法的返回值,得到檢視物件view,檢視物件決定如何渲染頁面,是轉發?重定向)
    • ContentNegotiatingViewResolver 組合所有的檢視解析器,
    • 如何定製ViewResolver?
    • 我們可以給容器中新增一個ViewResolver,然後這個ContentNegotiatingViewResolver 就會自動的將它組合進來
    •  
  • Support for serving static resources, including support for WebJars (covered later in this document)).靜態資原始檔夾李靜,webjars
  • Static index.html support. 靜態首頁訪問
  • Custom Favicon support (covered later in this document). favicon.ico,專案圖示
  • 自動註冊了 Converter, GenericConverter, and Formatter beans.
    • converter : 轉換器,作用就是:型別轉換使用(前臺提交的是資料都是文字,那麼在後臺需要轉換成int,string等型別),就需要使用converter
    • Formatter : 格式化器,他可以幫我們把字串按照我們指定的格式轉換為日期型別,例如 : 2017-12-12 >>> Date型別
	@Bean
	@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在檔案中配置日期格式化的規則
	public Formatter<Date> dateFormatter() {
		return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化元件
	}
  • 1
  • 2
  • 3
  • 4
  • 5
@Override
		public void addFormatters(FormatterRegistry registry) {
			for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
				registry.addConverter(converter);
			}
			for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
				registry.addConverter(converter);
			}
			for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
				registry.addFormatter(formatter);
			}
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

上面這個程式碼,告訴我們,如果我們想要新增自己的格式化器和轉換器,那麼,我們只需要將他們放入到容器中即可,它會自己把容器中所有的轉換器和格式化器掃描並且新增.

  • Support for HttpMessageConverters (covered later in this document).
    • HttpMessageConverters: http訊息轉化器,它是SpringMVC用來轉換請求和響應的,例如,我們其中一個處理器方法的返回值是user物件,我們想以json的格式寫出去,那麼我們就需要一個訊息轉換器.
    • HttpMessageConverters 是從容器中確定值的,是獲取容器中所有的HttpMessageConverters
    • 所以,還是那句話,如果我們自己要寫一個HttpMessageConverters ,我們只需要把它放入容器即可,你可以(@Bean,@Component等,將元件放入容器中)
  • Automatic registration of MessageCodesResolver (covered later in this document).
    • 定義錯誤程式碼生成規則的
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
    • 它的作用就是初始化 webdatabinder 把請求體中攜帶的資料,繫結封裝到javabean中,我們可以給容器中新增一個自定義的ConfigurableWebBindingInitializer ,來替換預設的
	@Override
		protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
//獲取容器中的ConfigurableWebBindingInitializer
			try {
				return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
			}
			catch (NoSuchBeanDefinitionException ex) {
//如果獲取失敗,會呼叫父類中的這個方法 ,它的功能就是初始化webdatabinder
				return super.getConfigurableWebBindingInitializer();
			}
		}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc

2 擴充套件springMVC

如果我們在實際開發過程中,springboot提供的預設的自動配置滿足不了實際的開發需求,我們需要自己定義,或者是擴充套件自動配置,怎麼辦?

1.編寫一個配置類(@Configuration),這個配置類的型別必須是WebMvcConfigurer ,並且不能標註@EnableWebMvc註解
特點:既保留了所有的自動配置,也能用我們的擴充配置

//使用webMvcConfigurer來擴充套件SpringMVC,它內部有很多的空方法,我們可以通過實現來擴充套件某個功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //傳送 /atguigu 請求,能夠訪問success頁面
        registry.addViewController("/atguigu").setViewName("success");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.如何來修改springboot的預設配置

模式:

1)、SpringBoot在自動配置很多元件的時候,先看容器中有沒有使用者自己配置的(@Bean、@Component)如果有就用使用者配置的,如果沒有,才自動配置;如果有些元件可以有多個(ViewResolver)將使用者配置的和自己預設的組合起來;
​ 2)、在SpringBoot中會有非常多的xxxConfigurer幫助我們進行擴充套件配置
​ 3)、在SpringBoot中會有很多的xxxCustomizer幫助我們進行定製配置

相關文章