SpringBoot快速入門
1.什么是SpringBoot
回顧什么是Spring?
- Spring是一個開源框架,2003 年興起的一個輕量級的Java 開發框架,作者:Rod Johnson 。
- Spring是為了解決企業級應用開發的復雜性而創建的,簡化開發。
Spring是如何簡化Java開發的?
為了降低Java開發的復雜性,Spring采用了以下4種關鍵策略:
1、基於POJO的輕量級和最小侵入性編程,所有東西都是bean;
2、通過IOC,依賴注入(DI)和面向接口實現松耦合;
3、基於切面(AOP)和慣例進行聲明式編程;
4、通過切面和模版減少樣式代碼,RedisTemplate,xxxTemplate;
什么是SpringBoot?
學過javaweb的同學就知道,開發一個web應用,從最初開始接觸Servlet結合Tomcat, 跑出一個Hello Wolrld程序,是要經歷特別多的步驟;后來就用了框架Struts,再后來是SpringMVC,到了現在的SpringBoot,過一兩年又會有其他web框架出現;你們有經歷過框架不斷的演進,然后自己開發項目所有的技術也在不斷的變化、改造嗎?建議都可以去經歷一遍;
言歸正傳,什么是SpringBoot呢,就是一個javaweb的開發框架,和SpringMVC類似,對比其他javaweb框架的好處,官方說是簡化開發,約定大於配置, you can “just run”,能迅速的開發web應用,幾行代碼開發一個http接口。
所有的技術框架的發展似乎都遵循了一條主線規律:從一個復雜應用場景 衍生 一種規范框架,人們只需要進行各種配置而不需要自己去實現它,這時候強大的配置功能成了優點;發展到一定程度之后,人們根據實際生產應用情況,選取其中實用功能和設計精華,重構出一些輕量級的框架;之后為了提高開發效率,嫌棄原先的各類配置過於麻煩,於是開始提倡“約定大於配置”,進而衍生出一些一站式的解決方案。
是的這就是Java企業級應用->J2EE->spring->springboot的過程。
隨着 Spring 不斷的發展,涉及的領域越來越多,項目整合開發需要配合各種各樣的文件,慢慢變得不那么易用簡單,違背了最初的理念,甚至人稱配置地獄。Spring Boot 正是在這樣的一個背景下被抽象出來的開發框架,目的為了讓大家更容易的使用 Spring 、更容易的集成各種常用的中間件、開源軟件;
Spring Boot 基於 Spring 開發,Spirng Boot 本身並不提供 Spring 框架的核心特性以及擴展功能,只是用於快速、敏捷地開發新一代基於 Spring 框架的應用程序。也就是說,它並不是用來替代 Spring 的解決方案,而是和 Spring 框架緊密結合用於提升 Spring 開發者體驗的工具。Spring Boot 以約定大於配置的核心思想,默認幫我們進行了很多設置,多數 Spring Boot 應用只需要很少的 Spring 配置。同時它集成了大量常用的第三方庫配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 應用中這些第三方庫幾乎可以零配置的開箱即用。
簡單來說就是SpringBoot其實不是什么新的框架,它默認配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
Spring Boot 出生名門,從一開始就站在一個比較高的起點,又經過這幾年的發展,生態足夠完善,Spring Boot 已經當之無愧成為 Java 領域最熱門的技術。
Spring Boot的主要優點:
- 為所有Spring開發者更快的入門;
- 開箱即用,提供各種默認配置來簡化項目配置;
- 內嵌式容器簡化Web項目;
- 沒有冗余代碼生成和XML配置的要求;
2.什么是微服務架構
什么是微服務?
微服務是一種架構風格,它要求我們在開發一個應用時,將單個應用程序開發為一組小服務的方法,每個小服務都在自己的進程中運行,並與輕量級機制(通常是 HTTP 資源 API)進行通信。簡單說:每個功能元素最終都是一個可獨立替換和獨立升級的軟件單元。
單體應用架構
-
所謂單體應用架構( all in one)是指,我們將一個應用的中的所有應用服務都封裝在一個應用中。
-
無論是ERP、CRM或是其他什么系統,你都把數據庫訪問,web訪問,等等各個功能放到一個war包內。
-
這樣做的好處是,易於開發和測試;也十分方便部罷;當需要擴展時,只需要將war復制多份,然后放到多個服務器上,再做個負載均衡就可以了。
-
單體應用架構的缺點是,哪怕我要修改一個非常小的地方,我都需要停掉整個服務,重新打包、部署這個應用war包。特別是對於一個大型應用,我們不可能吧所有內容都放在一個應用里面,我們如何維護、如何分工合作都是問題。
-
例如:Apache Dubbo
微服務架構
all in one的架構方式,我們把所有的功能單元放在一個應用里面。然后我們把整個應用部罷到服務器上。如果負載能力不行,我們將整個應用進行水平復制,進行擴展,然后在負載均衡。
所謂微服務架構,就是打破之前 all in one的架構方式,把每個功能元素獨立出來,肥獨立出來的功能元素的動態組合,需要的功能元素才去拿來組合,需要多一些時可以整合多個功能元素。所以微服務架構是對功能元素進行復制,而沒有對整個應用進行復制。
這樣做的好處是:
- 節省了調用資源。
- 每個功能元素的服都是一個可替換的、可獨立升吸的軟件代碼。
微服務技術棧有那些?
微服務技術條目 | 落地技術 |
---|---|
服務開發 | SpringBoot、Spring、SpringMVC等 |
服務配置與管理 | Netfix公司的Archaius、阿里的Diamond等 |
服務注冊與發現 | Eureka、Consul、Zookeeper等 |
服務調用 | Rest、PRC、gRPC |
服務熔斷器 | Hystrix、Envoy等 |
負載均衡 | Ribbon、Nginx等 |
服務接口調用(客戶端調用服務的簡化工具) | Fegin等 |
消息隊列 | Kafka、RabbitMQ、ActiveMQ等 |
服務配置中心管理 | SpringCloudConfig、Chef等 |
服務路由(API網關) | Zuul等 |
服務監控 | Zabbix、Nagios、Metrics、Specatator等 |
全鏈路追蹤 | Zipkin、Brave、Dapper等 |
數據流操作開發包 | SpringCloud Stream(封裝與Redis,Rabbit,Kafka等發送接收消息) |
時間消息總棧 | SpringCloud Bus |
服務部署 | Docker、OpenStack、Kubernetes等 |
如何構建微服務?
一個大型系統的微服務架構,就像一個復雜交織的神經網絡,每一個神經元就是一個功能元素,它們各自完成自己的功能,然后通過http相互請求調用。比如一個電商系統,查緩存、連數據庫、瀏覽頁面、結賬、支付等服務都是一個個獨立的功能服務,都被微化了,它們作為一個個微服務共同構建了個龐大的系統。如果修改其中的一個功能,只需要更新升級其中一個功能服務單元即可。
但是這種龐大的系統架構給部罷和運維帶來很大的難度。於是, spring為我們帶來了構建大型分布式微服務的全套、全程產品:
- 構建一個個功能獨立的微服務應用單元,可以使用 Spring boot,可以幫我們快速構建一個應用;
- 大型分布式網絡服務的調用,這部分由 Spring cloud來完成,實現分布式;
- 在分布式中間,進行流式數據計算、批處理,我們有 Spring cloud data flow。
- Spring為我們想清楚了整個從開始構建應用到大型分布式應用全流程方案。
3.第一個springboot程序
基本環境
- JDK 1.8
- maven 3.8.2
- Spring Boot 2.5.5
- IDEA 2021
創建基礎項目說明
-
Spring官方提供了非常方便的工具讓我們快速構建應用
-
Spring Initializr:https://start.spring.io/
項目創建方式一:使用Spring Initializr 的 Web頁面創建項目
- 填寫項目信息
- 點擊”Generate Project“按鈕生成項目;下載此項目
-
解壓項目包,並用IDEA以Maven項目導入,一路下一步即可,直到項目導入完畢。
-
如果是第一次使用,可能速度會比較慢,包比較多、需要耐心等待一切就緒。
報錯:
Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found
- 解決方法:添加版本號即可。
**項目創建方式二:**使用 IDEA 直接創建項目
-
創建一個新項目
-
選擇spring initalizr , 可以看到默認就是去官網的快速構建工具那里實現
-
填寫項目信息
-
選擇初始化的組件(初學勾選 Web 即可)
-
填寫項目路徑
- 等待項目構建成功
報錯:Cannot download ‘https://start.spring.io’: Connection reset
- 解決辦法:將
https
改為http
,或者使用阿里雲:https://start.aliyun.com/ ,地址直接替代。
項目結構分析:
通過上面步驟完成了基礎項目的創建。就會自動生成以下文件:
-
程序的主啟動類
-
一個 application.properties 配置文件
-
一個 測試類
-
一個 pom.xml
pom.xml 分析
打開pom.xml,看看Spring Boot項目的依賴:
<?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.5.5</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<groupId>com.github</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- web場景啟動器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot單元測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 剔除依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.5</version>
</dependency>
</dependencies>
<!-- 打包插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
編寫HTTP接口
- 在主程序的同級目錄下,新建一個controller包,一定要在同級目錄下,否則識別不到;
- 在包中新建一個HelloController類
package com.github.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** * @author subeiLY * @create 2021-10-21 18:23 */
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello,SpringBoot!";
}
}
- 編寫完畢后,從主程序啟動項目,瀏覽器發起請求,看頁面返回;控制台輸出了 Tomcat 訪問的端口號!
- 至此,就完成了一個web接口的開發,第一個Spring Boot完成。
將項目打成jar包,點擊 maven的 package
運行jar包命令:java -jar xxx.jar
附注
- 如何更改啟動時顯示的字符拼成的字母,SpringBoot呢?
- 也就是 banner 圖案; 只需一步:到項目下的 resources 目錄下新建一個banner.txt 即可。
- 圖案可以到:https://www.bootschool.net/ascii 這個網站生成,然后拷貝到文件中即可!
4.Springboot自動裝配原理
- 對於Maven項目,我們一般從pom.xml文件探究起。
1.Pom.xml
父依賴——主要是管理項目的資源過濾及插件!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
- 點進去,發現還有一個父依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.5</version>
</parent>
- 這里才是真正管理SpringBoot應用里面所有依賴版本的地方,SpringBoot的版本控制中心;
- spring-boot-dependencies:核心依賴在父工程中!所以,我們在寫或引入一些
- SpringBoot依賴時,不需要指定版本,因為本身就有這些版本倉庫。
啟動器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- springboot-boot-starter-xxx:就是spring-boot的場景啟動器;
- 比如 spring-boot-starter-web:幫我們導入了web模塊正常運行所依賴的組件;
- SpringBoot將所有的功能場景都抽取出來,變成一個個的starter (啟動器),只需要在項目中引入這些 starter即可,所有相關的依賴都會導入進來 , 我們要用什么功能就導入什么樣的場景啟動器即可;也可以自己自定義 starter。
2.主啟動類
默認的主啟動類——@SpringBootApplication
// @SpringBootApplication 標注一個主程序類, 說明這是一個Spring Boot應用
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 啟動一個服務
SpringApplication.run(DemoApplication.class, args);
}
}
- 進入這個注解,可以看到下面還有很多其他注解:
@ComponentScan
- 這個注解在Spring中對應XML配置中的元素。
- 作用:自動掃描並加載符合條件的組件或者bean , 將這個bean定義加載到IOC容器中。
依次按照如下層級點進去:
- @SpringBootConfiguration:spring boot的配置;
- @Configuration:spring配置類,對應Spring的xml 配置文件;
- @Component:說明這是一個Spring組件,負責啟動應用!
- @EnableAutoConfiguration:開啟自動配置功能;
- @AutoConfigurationPackage:自動配置包;
- @Import({Registrar.class}):自動配置包注冊;
- @Import({AutoConfigurationImportSelector.class}):自動導入組件;
- @AutoConfigurationPackage:自動配置包;
// 獲取所有的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
- 類中有一個這樣的方法
// 獲取候選的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 此處的getSpringFactoriesLoaderFactoryClass()方法
// 返回的就是我們最開始看的啟動自動導入配置文件的注解類;EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
- 這個方法又調用了 SpringFactoriesLoader 類的靜態方法!進入SpringFactoriesLoader類 loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 再次調用了 loadSpringFactories 方法
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 繼續點擊查看 loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 獲得classLoader, 返回可以看到這里得到的就是EnableAutoConfiguration標注的類本身
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
// 獲取一個資源 "META-INF/spring.factories"
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
// 將讀取到的資源遍歷,封裝成為一個Properties
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
- 發現一個多次出現的文件:
spring.factories
,全局搜索Ctrl+N它。
WebMvcAutoConfiguration——在上面的自動配置類隨便找一個打開看看,比如 : WebMvcAutoConfiguration
- 可以看到這些一個個的都是JavaConfig配置類,而且都注入了一些Bean,可以找一些自己認識的類,看着熟悉一下!
- 所以,自動配置真正實現是從classpath中搜尋所有的META-INF/spring.factories配置文件 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的配置項,通過反射實例化為對應標注了 @Configuration的JavaConfig形式的IOC容器配置類 , 然后將這些都匯總成為一個實例並加載到IOC容器中。
結論:
- SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取 EnableAutoConfiguration指定的值;
- 將這些值作為自動配置類導入容器,自動配置類就生效,幫我們進行自動配置工作;
- 整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;
- 它會把所有需要導入的組件,以類名的方式返回,這些組件就會被添加到容器;
- 容器中也會存在非常多的 xxxAuto Configuration的文件(@Bean),就是這些類給容器中導入了這個場景需要的所有組件並自動配置,@Configuration, JavaConfig;
- 有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作!
3.SpringApplication
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 啟動一個服務
SpringApplication.run(DemoApplication.class, args);
}
}
- 該方法主要分兩部分,一部分是SpringApplication的實例化,二是run方法的執行。
這個類主要做了以下四件事情:
- 推斷應用的類型是普通的項目還是Web項目;
- 查找並加載所有可用初始化器 , 設置到initializers屬性中;
- 找出所有的應用程序監聽器,設置到listeners屬性中;
- 推斷並設置main方法的定義類,找到運行的主類。
查看構造器:
run方法:
5.Yaml語法
配置文件
SpringBoot使用一個全局的配置文件 , 配置文件名稱是固定的。
- application.properties
- 語法結構:key=value
- application.yml
- 語法結構:key:空格 value
配置文件的作用 :修改SpringBoot自動配置的默認值,因為SpringBoot在底層都給我們自動配置好了;
比如我們可以在配置文件中修改Tomcat 默認啟動的端口號!測試一下!
yaml 概述
-
YAML是 “YAML Ain’t a Markup Language” (YAML不是一種標記語言)的遞歸縮寫。
-
在開發的這種語言時,YAML 的意思其實是:“Yet Another Markup Language”(仍是一種標記語言)
-
這種語言以數據做為中心,而不是以標記語言為重點!
-
以前的配置文件,大多數都是使用xml來配置;比如一個簡單的端口配置,我們來對比下yaml和xml
-
傳統xml配置:
<server>
<port>8081</port>
</server>
- yaml配置:
server:
port: 8081
yml基礎語法
1、空格不能省略
2、以縮進來控制層級關系,只要是左邊對齊的一列數據都是同一個層級的。
3、屬性和值的大小寫都是十分敏感的。
- 字面量:普通的值 [ 數字,布爾值,字符串 ]
- 字面量直接寫在后面就可以 , 字符串默認不用加上雙引號或者單引號;
k: v
注意:
- “ ” 雙引號,不會轉義字符串里面的特殊字符 , 特殊字符會作為本身想表示的意思; 比如 : name: “github\n subei” 輸出 : github 換行 subei
- ‘’ 單引號,會轉義特殊字符 , 特殊字符最終會變成和普通字符一樣輸出 比如 : name: ‘github\n subei’ 輸出 : github\n subei
# k=v
# 普通的key-value
name: github
# 對象
student:
name: github
age: 18
# 行內寫法
student2: {name: github,age: 18}
# 數組 用 - 值表示數組中的一個元素
pets:
- cat
- dog
- pig
pets2: [cat,dog,pig]
6.給屬性賦值的幾種方式
- yaml文件在於:可以直接給實體類直接注入匹配值!
Yaml注入配置文件
- 在springboot項目中的resources目錄下新建一個文件 application.yaml
- 編寫一個實體類Dog;
- getset方法,無參有參——快捷鍵:alt+insert
package com.github.pojo;
import org.springframework.stereotype.Component;
@Component // 注冊bean到容器中
public class Dog {
private String name;
private Integer age;
// 有參無參構造、get、set方法、toString()方法
}
- 思考:我們原來是如何給bean注入屬性值的! @Value,給寵物類測試一下:
@Component
public class Dog {
@Value("旺財")
private String name;
@Value("3")
private Integer age;
// 有參無參構造、get、set方法、toString()方法
}
- 在SpringBoot的測試類下注入寵物類輸出一下;
@SpringBootTest
class Springboot02ConfigApplicationTests {
@Autowired // 將寵物類自動注入進來
Dog dog;
@Test
void contextLoads() {
System.out.println(dog); // 打印一下寵物對象
}
}
- 輸出成功,@Value注入成功,這是之前的實現方式。
- 再編寫一個復雜一點的實體類:Person 類
@Component
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
// 有參無參構造、get、set方法、toString()方法
}
- 使用yaml配置的方式進行注入,注意區別和優勢,編寫一個yaml配置。
person:
name: subeiLY
age: 18
happy: true
birth: 2002/03/04
maps: {k1: v1,k2: v2}
lists:
- code
- book
- music
dog:
name: 來福
age: 2
- 將person類注入到類中!
/* @ConfigurationProperties作用: 將配置文件中配置的每一個屬性的值,映射到這個組件中; 告訴SpringBoot將本類中的所有屬性和配置文件中相關的配置進行綁定 參數 prefix = “person” : 將配置文件中的person下面的所有屬性一一對應 */
/** * 注冊bean */
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
// 有參無參構造、get、set方法、toString()方法
}
- IDEA 提示,springboot配置注解處理器沒有找到,讓我們看文檔,我們可以查看文檔,找到一個依賴!但它打不開,不影響。導入如下配置即可。
<!-- 導入配置文件處理器,配置文件進行綁定就會有提示,需要重啟 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
- 確認以上配置都OK之后,去測試類中測試一下:
@SpringBootTest
class Springboot02ConfigApplicationTests {
@Autowired // 將人類自動注入進來
Person person;
@Test
void contextLoads() {
System.out.println(person); // 打印一下人物對象
}
}
- 結果:所有值全部注入成功!
yaml配置注入到實體類完全OK!
加載指定配置文件
-
@PropertySource :加載指定的配置文件;
-
@configurationProperties:默認從全局配置文件中獲取值;
- 在resources目錄下新建一個person.properties文件。
name=subeiLY
- 在我們的代碼中指定加載person.properties文件。
@Component
//@ConfigurationProperties(prefix = "person")
// 指定配置文件綁定
@PropertySource(value = "classpath:application.properties")
public class Person {
@Value("${name}")
private String name;
......
}
- 再次輸出測試一下:指定配置文件綁定成功!
配置文件占位符
person:
name: subeiLY${random.uuid} # 隨機uuid
age: ${random.int} # 隨機int
happy: true
birth: 2002/03/04
maps: {k1: v1,k2: v2}
lists:
- code
- book
- music
dog:
# 引用person.hello 的值,如果不存在就用 :后面的值,即 other,然后拼接上_來福
name: ${person.hello:other}_來福
age: 2
回顧properties配置
-
上面采用的yaml方法都是最簡單的方式,開發中最常用的;也是springboot所推薦的!
-
對於其他的實現方式,道理都是相同的;寫還是那樣寫;配置文件除了yml還有我們之前常用的properties。
-
【注意】properties配置文件在寫中文的時候,會有亂碼 , 我們需要去IDEA中設置編碼格式為UTF-8;
- settings–>FileEncodings 中配置;
測試步驟:
- 新建一個實體類User
@Component //注冊bean
public class User {
private String name;
private int age;
private String sex;
// 有參無參構造、get、set方法、toString()方法
}
- 編輯配置文件 application.properties
user1.name=subei
user1.age=16
user1.sex=男
- 在User類上使用@Value來進行注入!
@Component //注冊bean
@PropertySource(value = "classpath:application.properties")
public class User {
@Value("${user1.name}") //從配置文件中取值
private String name;
@Value("#{8*2}") // #{SPEL} Spring表達式
private int age;
@Value("男") // 字面量
private String sex;
}
- Springboot測試
@SpringBootTest
class Springboot02ConfigApplicationTests {
@Autowired
User user;
@Test
void contextLoads() {
System.out.println(user);
}
}
對比小結
- @Value這個使用起來並不友好!我們需要為每個屬性單獨注解賦值,比較麻煩;我們來看個功能對比圖:
- @ConfigurationProperties只需要寫一次即可 , @Value則需要每個字段都添加;
- 松散綁定:這個什么意思呢? 比如我的yml中寫的last-name,這個和lastName是一樣的, - 后面跟着的字母默認是大寫的。這就是松散綁定。可以測試一下!
- JSR303數據校驗,這個就是我們可以在字段是增加一層過濾器驗證,可以保證數據的合法性;
- 復雜類型封裝,yml中可以封裝對象,使用value就不支持。
結論:
- 配置yml和配置properties都可以獲取到值 , 強烈推薦 yml;
- 如果我們在某個業務中,只需要獲取配置文件中的某個值,可以使用一下 @value;
- 如果說,我們專門編寫了一個JavaBean來和配置文件進行一一映射,就直接@configurationProperties,不要猶豫!
7.JSR303數據校驗
- 在pom.xml中添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.5.6</version>
</dependency>
- Springboot中可以用@validated來校驗數據,如果數據異常則會統一拋出異常,方便異常中心統一處理。先寫個注解讓name只能支持Email格式;
@Component
@ConfigurationProperties(prefix = "person")
@Validated // 數據校驗
public class Person {
// name必須是郵箱格式
@Email(message="郵箱格式錯誤")
private String name;
......
}
- 運行結果:default message [name];
- 使用數據校驗,可以保證數據的正確性; 下面列出一些常見的使用:
@NotNull(message="名字不能為空")
private String userName;
@Max(value=120,message="年齡最大不能查過120")
private int age;
@Email(message="郵箱格式錯誤")
// 空檢查
@Null 驗證對象是否為null
@NotNull 驗證對象是否不為null, 無法查檢長度為0的字符串
@NotBlank 檢查約束字符串是不是Null還有被Trim的長度是否大於0,只對字符串,且會去掉前后空格.
@NotEmpty 檢查約束元素是否為NULL或者是EMPTY.
// Booelan檢查
@AssertTrue 驗證 Boolean 對象是否為 true
@AssertFalse 驗證 Boolean 對象是否為 false
// 長度檢查
@Size(min=, max=) 驗證對象(Array,Collection,Map,String)長度是否在給定的范圍之內
@Length(min=, max=) string is between min and max included.
// 日期檢查
@Past 驗證 Date 和 Calendar 對象是否在當前時間之前
@Future 驗證 Date 和 Calendar 對象是否在當前時間之后
@Pattern 驗證 String 對象是否符合正則表達式的規則
.......等等
除此以外,我們還可以自定義一些數據校驗規則
8.多環境配置及配置文件位置
- profile是Spring對不同環境提供不同配置功能的支持,可以通過激活不同的環境版本,實現快速切換環境;
多配置文件
- 在主配置文件編寫的時候,文件名可以是 application-{profile}.properties/yml , 用來指定多個環境版本;
- 例如:
- application-test.properties 代表測試環境配置;
- application-dev.properties 代表開發環境配置;
- 但是Springboot並不會直接啟動這些配置文件,它默認使用application.properties主配置文件; 我們需要通過一個配置來選擇需要激活的環境:
# 比如在配置文件中指定使用dev環境,我們可以通過設置不同的端口號進行測試;
# 我們啟動SpringBoot,就可以看到已經切換到dev下的配置了;
spring.profiles.active=dev
yml的多文檔塊
- 和properties配置文件中一樣,但是使用yml去實現不需要創建多個配置文件,更方便 !
server:
port: 8081
# 選擇要激活那個環境塊
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev # 配置環境的名稱
---
server:
port: 8083
spring:
profiles: test # 配置環境的名稱
- 注意:如果yml和properties同時都配置了端口,並且沒有激活其他環境 , 默認會使用properties配置文件的!
配置文件加載位置
- 官方外部配置文檔參考
- springboot 啟動會掃描以下位置的application.properties或者application.yml文件作為Spring boot的 默認配置文件。
優先級1:項目路徑下的config文件夾配置文件
優先級2:項目路徑下配置文件
優先級3:資源路徑下的config文件夾配置文件
優先級4:資源路徑下配置文件
- SpringBoot會從這四個位置全部加載主配置文件;互補配置;
- 我們在最低級的配置文件中設置一個項目訪問路徑的配置來測試互補問題;
# 配置項目的訪問路徑
server.servlet.context-path=/github
指定位置加載配置文件
- 可以通過spring.config.location來改變默認的配置文件位置;
- 項目打包好以后,我們可以使用命令行參數的形式,啟動項目的時候來指定配置文件的新位置;這種情況,一般是后期運維做的多,相同配置,外部指定的配置文件優先級最高。
java -jar spring-boot-config.jar --
spring.config.location=F:/application.properties
9.自動配置原理再理解
分析自動配置原理
- 以HttpEncodingAutoConfiguration(Http編碼自動配置)為例解釋自動配置原理。
// 表示這是一個配置類,和以前編寫的配置文件一樣,也可以給容器中添加組件;
@Configuration
// 啟動指定類的ConfigurationProperties功能;
// 進入這個HttpProperties查看,將配置文件中對應的值和HttpProperties綁定起來;
// 並把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class})
// Spring底層@Conditional注解
// 根據不同的條件判斷,如果滿足指定的條件,整個配置類里面的配置就會生效;
// 這里的意思就是判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)
// 判斷當前項目有沒有這個類CharacterEncodingFilter;SpringMVC中進行亂碼解決的過濾器;
@ConditionalOnClass({CharacterEncodingFilter.class})
// 判斷配置文件中是否存在某個配置:spring.http.encoding.enabled;
// 如果不存在,判斷也是成立的
// 即使我們配置文件中不配置pring.http.encoding.enabled=true,也是默認生效的;
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
// 他已經和SpringBoot的配置文件映射了
private final Encoding properties;
// 只有一個有參構造器的情況下,參數的值就會從容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
// 給容器中添加一個組件,這個組件的某些值需要從properties中獲取
@Bean
@ConditionalOnMissingBean // 判斷容器沒有這個組件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new
OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframew
ork.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframe
work.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
// 。。。。。。。
}
總結 : 根據當前不同的條件判斷,決定這個配置類是否生效!
- 一但這個配置類生效;這個配置類就會給容器中添加各種組件;
- 這些組件的屬性是從對應的properties類中獲取的,這些類里面的每一個屬性又是和配置文件綁定的;
- 所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝着;
- 配置文件能配置什么就可以參照某個功能對應的這個屬性類
// 從配置文件中獲取指定的值和bean的屬性進行綁定
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
// .....
}
- 去配置文件里面試試前綴,看提示!
要點:
-
SpringBoot啟動會加載大量的自動配置類。
-
我們看我們需要的功能有沒有在SpringBoot默認寫好的自動配置類當中;
-
我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)。
-
給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們只需要在配置文件中 指定這些屬性的值即可;
- xxxxAutoConfigurartion:自動配置類;給容器中添加組件;
- xxxxProperties:封裝配置文件中相關屬性。
了解:@Conditional
- 關注一個細節問題:自動配置類必須在一定的條件下才能生效。
- @Conditional派生注解(Spring注解版原生的@Conditional作用)
- 作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置配里面的所有內容才生效;
- 那么多的自動配置類,必須在一定的條件下才能生效;也就是說,加載了這么多的配置類,但不是所有的都生效了。
- 如何知道哪些自動配置類生效?
- 可以通過啟用 debug=true屬性;來讓控制台打印自動配置報告,這樣就可以很方便的知道哪些自動配置類生效。
# 開啟springboot的調試類
debug=true
- Positive matches:(自動配置類啟用的:正匹配)
- Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負匹配)
- Unconditional classes: (沒有條件的類)
10.自定義starter
- 嘗試自定義一個啟動器。
說明
- 啟動器模塊是一個 空 jar 文件,僅提供輔助性依賴管理,這些依賴可能用於自動裝配或者其他類庫。
命名歸約:
- 官方命名:
- 前綴: spring-boot-starter-xxx
- 比如:spring-boot-starter-web…
- 自定義命名:
- xxx-spring-boot-starter
- 比如:mybatis-spring-boot-starter
編寫啟動器
- 在IDEA中新建一個空項目 spring-boot-starter-diy
- 新建一個普通Maven模塊:spring-boot-starter
- 新建一個Springboot模塊:spring-boot-starter-autoconfigure
- 在 starter 中 導入 autoconfigure 的依賴!spring-boot-starter無需編寫什么代碼,只需讓該工程引入hello-spring-boot-starter-autoconfigure依賴:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--引入自動配制包-->
<dependency>
<groupId>com.github</groupId>
<artifactId>spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
spring-boot-starter-autoconfigure
的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.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.github</groupId>
<artifactId>spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-starter-autoconfigure</name>
<description>spring-boot-starter-autoconfigure</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
- 將 autoconfigure 項目下多余的文件都刪掉,Pom中只留下一個 starter,這是所有的啟動器基本配置。
- 編寫一個個人的服務
package com.github;
public class HelloService {
HelloProperties helloProperties;
public HelloProperties getHelloProperties() {
return helloProperties;
}
public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
public String sayHello(String name){
return helloProperties.getPrefix() + name +
helloProperties.getSuffix();
}
}
- 編寫HelloProperties 配置類
package com.github;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "github.hello")
public class HelloProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
- 編寫自動配置類並注入bean,測試!
package com.github;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * 自動配制類 */
@Configuration
/** * EnableConfigurationProperties的作用 * 1、開啟HelloProperties配置綁定功能 * 2、把這個HelloProperties這個組件自動注冊到容器中 */
@ConditionalOnWebApplication //web應用生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean // @Bean 用在方法上,告訴Spring容器,你可以從下面這個方法中拿到一個Bean
public HelloService helloService(){
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}
- 在resources編寫一個自己的 META-INF\spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.HelloServiceAutoConfiguration
- 安裝到maven倉庫中!
新建項目測試我們自己的寫的啟動器
-
新建一個SpringBoot 項目。
-
導入我們自己寫的啟動器。
<!--引入自動配制包-->
<dependency>
<groupId>com.github</groupId>
<artifactId>spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 編寫一個 HelloController 進行測試自己的寫的接口!
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@RequestMapping("/hello")
public String hello(){
return helloService.sayHello("whh");
}
}
- 編寫配置文件 application.properties
github.hello.prefix="qqqq"
github.hello.suffix="pppp"
- 啟動項目進行測試 !
下一篇:SpringBoot開發單體應用