Spring Boot應(yīng)用回顧
約定由于配置
約定優(yōu)于配置:按約定編程狡门,是一種軟件設(shè)計規(guī)范视哑。
什么是Spring Boot
Spring boot官網(wǎng)
使用Spring Boot可以簡單的創(chuàng)建一個基于Spring 應(yīng)用的獨立的產(chǎn)品級的應(yīng)用家厌。
Spring Boot 的目的是簡化Spring應(yīng)用的開發(fā)壁却,盡可能的減少配置揖闸,盡快的讓你的Spring應(yīng)用跑起來蝌矛。
- Spring Boot 是Pivotal團(tuán)隊研發(fā)
- SpringBoot是基于Spring 4.0設(shè)計的会傲。
- Spring Boot集成了大量的框架混卵,使得依賴包的版本沖突和引用不穩(wěn)定性得到了很好的解決。
Spring Boot 就是一種快速使用Spring的方式恶阴,并且可以省去繁瑣的配置诈胜。
Spring Boot主要特性
- Spring Boot Starter(起步依賴):將常用依賴分組整合,將其合并到一個依賴中冯事,這樣就可以一次性添加到Maven或Gradle構(gòu)建中焦匈。
-
JavaConfig方式配置
Spring發(fā)展史:
image
Spring Boot是基于Spring4.0設(shè)計的,當(dāng)時的Spring4.0已經(jīng)支持了JavaConfig方式桅咆。
-
自動配置:利用Spring對條件化配置的支持括授,合理地推測應(yīng)用所需的bean并自動化配置他們。
image.png SpringBoot內(nèi)置Servlet容器岩饼,部署簡單。
Spring Boot案例實現(xiàn)
目標(biāo):使用Spring initializr創(chuàng)建springboot工程薛夜,編寫Controller籍茧,并成功請求Controller,返回響應(yīng)梯澜。
1)創(chuàng)建Spring Boot工程:
填寫項目基本信息
選擇依賴
創(chuàng)建完成之后的目錄結(jié)構(gòu):
2)創(chuàng)建 Controller
package com.xdf.springbootdemo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xdf
* @version 1.0
* @date Create in 9:32 2021/6/23
* @description 測試Controller
* @modifiedBy
*/
@RestController
public class DemoController {
@RequestMapping("/demo")
public String demo() {
return "demo";
}
}
注意:controller必須放在SpringBootDemoApplication啟動類下面的包中寞冯,也就是說controller的包路徑必須必啟動了深渴析。
因為SpringBoot包掃描的根目錄是啟動類所在包。
3)啟動測試
通過SpringBootDemoApplication啟動類中的main方法啟動應(yīng)用吮龄。
訪問http://localhost:8080/demo觀察結(jié)果:
思考:
- starter是什么俭茧?我們?nèi)绾稳ナ褂眠@些starter?
- 為什么包掃描只會掃描核心啟動類所在的包及其子包
- 在springBoot啟動的過程中漓帚,是如何完成自動裝配的母债?
- 內(nèi)嵌Tomcat是如何被創(chuàng)建及啟動的?
- 使用了web場景對應(yīng)的starter尝抖,springmvc是如何自動裝配毡们?
Spring Boot熱部署
在修改代碼后需要驗證時,需要重啟項目昧辽。Spring開發(fā)團(tuán)隊提供了一個插件:spring-boot-devtools衙熔,解決啟動緩慢問題。(開發(fā)階段提高效率搅荞,不建議線上使用)
熱部署演示
引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
配置idea自動編譯:
ctrl+alt+shift+/調(diào)出窗口:
選擇registry红氯,在新彈出的窗口中找到并勾選下面的選項:
重啟項目,調(diào)用請求http://localhost:8080/demo查看
修改Controller:
@RestController
public class DemoController {
@RequestMapping("/demo")
public String demo() {
return "熱部署 demo";
}
}
稍等一會咕痛,再次調(diào)用http://localhost:8080/demo
到此就實現(xiàn)了應(yīng)用熱部署演示痢甘。
熱部署原理分析
Spring官網(wǎng)對自動部署的介紹:
自動部署插件會監(jiān)聽classpath下面的文件,一旦文件有變化暇检,就會重啟产阱。重啟的唯一方式就是更新classpath。
所以它的觸發(fā)條件是classpath下面的文件變化块仆,因此需要配合idea的自動編譯功能构蹬。自動編譯后,會將編譯后的class文件更新到classpath下面悔据,觸發(fā)spring-boot-devtools的重啟功能庄敛,完成應(yīng)用更新。
spring-boot-devtools的熱部署功能是怎么實現(xiàn)的科汗?
官方描述:
工具是通過兩個classloader來實現(xiàn)的藻烤,一個classloader加載不變的類,一個classloader是加載我們自己寫的class头滔。
兩個類加載器分別加載機(jī)制驗證:
編寫測試代碼
package com.xdf.springbootdemo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.DispatcherServlet;
/**
* @author xdf
* @version 1.0
* @date Create in 10:12 2021/6/23
* @description spring boot devtools類加載驗證
* @modifiedBy
*/
@Component
public class DevtoolTest implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(DevtoolTest.class);
@Override
public void afterPropertiesSet() throws Exception {
log.info("guava jar classlodaer:${}" , DispatcherServlet.class.getClassLoader());
log.info("DevtoolsTest classlodaer:${}" , this.getClass().getClassLoader());
}
}
輸出結(jié)果驗證怖亭,發(fā)現(xiàn)DispatchorServlet是AppClassLoader加載的。而我們自己寫的DevtoolsTest類的加載器是RestartClassloader坤检。
如果不引入Devtools兴猩,他們的類加載器都是AppClassloader。
Spring Boot 熱部署排除資源
某些資源在更改后不一定需要觸發(fā)重新啟動早歇。例如倾芝,Thymeleaf模板可以就地編輯讨勤。默認(rèn)情況下,改變資源 /META-INF/maven 晨另, /META-INF/resources 潭千,/resources , /static 借尿, /public 刨晴, 或 /templates 不觸發(fā)重新啟動,但確會觸發(fā)現(xiàn)場重裝垛玻。如果要自定義這些排除項割捅,則可以使用該 spring.devtools.restart.exclude 屬性。例如帚桩,僅排除 /static 亿驾, /public 您將設(shè)置以下屬性:
找到devtools jar包,打開spring.factories文件
找到LocalDevToolsAutoConfiguration類
點擊進(jìn)去:
找到DevToolsProperties類账嚎,點擊進(jìn)入
在這個類中默認(rèn)排除了:
如果我們想更改默認(rèn)值
我們現(xiàn)在找到prefix:
并且這個類里面有一個內(nèi)部類Restart莫瞬,Restart方法中的exclude是我們需要修改的值。
并且這個屬性提供了setter方法:
因此郭蕉,我們需要在配置文件中配置的屬性為:
spring.devtools.restart.exclude
因此我們可以在application.propterties中這樣配置:
spring.devtools.restart.exclude=static/**,templates/**
Spring Boot 全局配置文件
全局配置文件概述及優(yōu)先級
Spring Boot使用一個application.properties和application.yml文件作為全局配置文件疼邀。
配置文件的加載目錄:
分別對應(yīng):
–file:./config/
–file:./
–classpath:/config/
–classpath:/
同時,序號也是配置文件的加載順序召锈。
其中旁振,序號越大的配置文件的屬性優(yōu)先級越低。(后面加載的配置文件不會覆蓋前面配置的配置文件)
不同配置文件的不同屬性涨岁,會相互互補(bǔ)拐袜。
properties和yml文件在相同目錄下,誰的配置優(yōu)先級高梢薪?
springboot在2.4.0之前默認(rèn)properties文件的優(yōu)先級更高蹬铺,在2.4.0之后是yml文件優(yōu)先級更高。
如果在2.4.0z之后的版本還是希望是properties優(yōu)于yml秉撇,可以配置:
spring.config.use-legacy-processing = true
自定義配置文件名:
$ java -jar myproject.jar --spring.config.name=myproject
指定配置文件:
java -jar run-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.properties
和其他配置文件互補(bǔ)甜攀。
application.properties配置文件
Spring boot在啟動的時候會加載application.properties文件。除了框架的配置信息之外琐馆。我們還可以自定義配置屬性注入规阀。
Person:
package com.xdf.springbootdemo.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author xdf
* @date Create in 13:45 2021/6/23
* @description 人
* @modifiedBy
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int id;
/**
* 名字
*/
private String name;
/**
* 愛好
*/
private List hobby;
/**
* 家庭成員
*/
private String[] family;
/**
*
*/
private Map map;
/**
* 寵物
*/
private Pet pet;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobby() {
return hobby;
}
public void setHobby(List hobby) {
this.hobby = hobby;
}
public String[] getFamily() {
return family;
}
public void setFamily(String[] family) {
this.family = family;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", hobby=" + hobby +
", family=" + Arrays.toString(family) +
", map=" + map +
", pet=" + pet +
'}';
}
}
Pet:
package com.xdf.springbootdemo.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author xdf
* @version 1.0
* @date Create in 13:45 2021/6/23
* @description 寵物
* @modifiedBy
*/
@Component
@ConfigurationProperties(prefix = "pet")
public class Pet {
private String type;
private String name;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet{" +
"type='" + type + '\'' +
", name='" + name + '\'' +
'}';
}
}
application.properties
person.id=1
person.name=tom
person.hobby=吃飯,睡覺瘦麸,打豆豆
person.family=爸爸
person.map.ley1=v1
person.map.ley2=v2
person.pet.type=dog
person.pet.name=大黃
為了idea能給自定義屬性注入提示姥敛,需要引入maven依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
application.yml配置文件
- yml與xml比,少了結(jié)構(gòu)性代碼瞎暑,數(shù)據(jù)更直接
- 相比properties文件更簡潔
- 擴(kuò)展名為yml或yaml
- yml文件使用“key:(空格)”格式配置屬性彤敛,使用縮進(jìn)控制層級關(guān)系。
1)普通類型配置
person:
id: 1
name: tom
2)list/數(shù)組類型配置
person:
hobby:
- 吃飯
- 睡覺
- 打豆豆
或
person:
hobby:
吃飯
睡覺
打豆豆
或
person:
hobby: [吃飯,睡覺,打豆豆]
- map/對象類型配置
person:
map:
k1: v1
k2: v2
或
person:
map: {k1: v1, k2: v2}
屬性注入
使用Spring Boot全局配置文件時:
如果配置的是Spring Boot的已有屬性了赌,Spring Boot會自動掃描讀取這些配置并覆蓋默認(rèn)配置墨榄。
如果是自定義屬性,必須在程序中注入這些信息才能生效勿她。
屬性注入常用注解
- @Configration:聲明一個類作為配置類
- @Bean:將方法返回值作為bean放入IoC容器
- @Value:屬性注入
- @ConfigurationProperties:批量屬性注入
- @PropertySource:指定外部屬性文件袄秩。
@Value屬性值注入
數(shù)據(jù)庫數(shù)據(jù)源配置示例:
引入maven依賴
<dependency>
<groupId>com.github.drtrang</groupId>
<artifactId>druid-spring-boot2-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.24</version>
</dependency>
自定義配置屬性:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring
jdbc.username=root
jdbc.password=123456
自定義配置類:
package com.xdf.springbootdemo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author xdf
* @version 1.0
* @date Create in 14:34 2021/6/23
* @description jdbc配置類
* @modifiedBy
*/
@Configuration
public class JdbcConfig {
/**
* jdbc驅(qū)動
*/
@Value("${jdbc.driverClassName}")
private String driverClassName;
/**
* 數(shù)據(jù)庫url
*/
@Value("${jdbc.url}")
private String url;
/**
* 用戶名
*/
@Value("${jdbc.username}")
private String username;
/**
* 密碼
*/
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "JdbcConfig{" +
"driverClassName='" + driverClassName + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
通過@Configuration注解讓Spring掃描配置類,使用@value注入屬性值逢并。使用@Bean注解之剧,將dataSoure方法的返回值放到IoC容器。
@ConfigurationProperties批量注入
使用批量注入方式重新配置數(shù)據(jù)源:
maven依賴注入:
<dependency>
<groupId>com.github.drtrang</groupId>
<artifactId>druid-spring-boot2-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.24</version>
</dependency>
自定義配置屬性:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring
jdbc.username=root
jdbc.password=123456
自定義配置類:
package com.xdf.springbootdemo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author xdf
* @version 1.0
* @date Create in 14:57 2021/6/23
* @description 批量注入方式配置jdbc數(shù)據(jù)源
* @modifiedBy
*/
@Configuration
@ConfigurationProperties(prefix = "jdbc")
public class BatchJdbcConfig {
/**
* 驅(qū)動類名
*/
private String driverClassName;
/**
* jdbc url
*/
private String url;
/**
* 用戶名
*/
private String username;
/**
* 密碼
*/
private String password;
/**
* 將數(shù)據(jù)源bean放到ioc容器
* @return dataSource
*/
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
/**
* 批量方式必須實現(xiàn)setter方法
*/
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* 方便打印觀察
* @return
*/
@Override
public String toString() {
return "BatchJdbcConfig{" +
"driverClassName='" + driverClassName + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
三方配置
引入的三方j(luò)ar包中的類是無法修改的砍聊,沒辦法在原本的類上面去做屬性注入背稼。但是我們也有辦法做屬性注入。下面看個例子:
三方類(假設(shè)不能修改):
package com.xdf.springbootdemo.pojo;
/**
* @author xdf
* @version 1.0
* @date Create in 15:20 2021/6/23
* @description 模擬三方j(luò)ar包中的類
* @modifiedBy
*/
public class AnotherComponent {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "AnotherComponent{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
我們需要對name和address進(jìn)行屬性注入玻蝌。
編寫配置類:
/**
* @author xdf
* @version 1.0
* @date Create in 15:22 2021/6/23
* @description 三方j(luò)ar包中的類的屬性注入
* @modifiedBy
*/
@Configuration
public class AnotherConfig {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
三方類通過@Bean放入Ioc容器蟹肘。
在@Bean方法上面使用@ConfigurationProperties注解標(biāo)記
屬性配置:
another.name=anothername
another.address=address
例如:
@Data
@Component
@ConfigurationProperties("acme.my-person.person")
public class OwnerProperties {
private String firstName;
}
acme:
my-person:
person:
first-name: 泰森
松散綁定
Spring Boot 支持使用一些寬松的規(guī)則將屬性綁定到@ConfigurationProperties bean。
@ConfigurationProperties VS @Value
Spring Boot 日志框架
日志框架介紹
日志框架設(shè)計思想
市面上常見的日志框架:
JCL俯树、SLF4J帘腹、Jboss-logging、log4j许饿、log4j2阳欲、logback等。
其中細(xì)分為:
- Jboss-logging 是應(yīng)用在特殊場景的日志抽象層框架陋率,我們一般不用
- JCL是Commons包中提供的日志框架球化,spring默認(rèn)在用,但不維護(hù)了
- SLF4J是log4j翘贮、logback的抽象層赊窥,三個抽象層中,前兩個不用狸页,一般就用SLF4J
- jul是jdk提供的日志框架锨能,功能不夠強(qiáng)大
- log4j存在性能問題,作者開發(fā)了升級版logback
- logback跟log4j都遵循SLF4J的抽象層框架規(guī)范芍耘,性能比log4j強(qiáng)大址遇,推薦使用
- log4j2是Apache開發(fā)的另外一個日志框架,但是應(yīng)用得比較少斋竞,不推薦
Spring boot采用的是SLF4J+logback方案倔约。
SLF4J的使用
slf4j官方示例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
下圖是SLF4J的官方提供的示例圖。SEF4J框架作為其他日志實現(xiàn)框架的門面直接應(yīng)用到應(yīng)用程序中坝初。
如果不引入日志實現(xiàn)框架浸剩,日志無法輸出钾军。logbank,slf4j-simple绢要,slf4j-nop是遵循slf4j規(guī)范的日志實現(xiàn)框架吏恭,只要引入jar包就能使用。
而log4j重罪、jdk日志框架樱哼,在開發(fā)的時候還沒有slf4j規(guī)范,因此需要適配層slf4j-log412剿配、slf4j-jdk14來進(jìn)行適配搅幅。
注意:由于每一個日志的實現(xiàn)框架都有自己的配置文件,所以在使用 SLF4j 之后呼胚,配置文件還是要使用實現(xiàn)日志框架的配置文件茄唐。
統(tǒng)一日志框架的使用
不同的三方框架使用不同的日志框架,在應(yīng)用中進(jìn)行整合的時候砸讳,怎么使用統(tǒng)一的日志框架琢融?
如果引入的三方框架也引入了日志實現(xiàn)類,那么
- 在引入三方框架的時候要排除三方框架引入的日志實現(xiàn)框架簿寂。
- 引入一個轉(zhuǎn)換層(jcl-over-slf4j漾抬、log4j-over-slf4j、jul-to-slf4j)
- 引入我們選擇的日志實現(xiàn)框架
Spring Boot中的日志關(guān)系
1)排除其他日志框架
在Spring Boot的spring-boot-dependencies中常遂,將三方框架的其他日志實現(xiàn)框架進(jìn)行了排除纳令。
2)統(tǒng)一框架引入替換包
Spring boot 在spring-boot-starter-logging中引入了自己選擇的日志實現(xiàn)框架
引入日志框架轉(zhuǎn)換層
日志相關(guān)Maven依賴關(guān)系:
Spring Boot 的日志使用
Spring Boot日志級別測試:
@Test
void testLog() {
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.trace("trace 日志");
logger.debug("debug 日志");
logger.info("info 日志");
logger.warn("warn 日志");
logger.error("error 日志");
}
打印
Spring Boot默認(rèn)日志級別是info級別
Spring Boot的日志格式是
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
# %d{yyyy-MM-dd HH:mm:ss.SSS} 時間
# %thread 線程名稱
# %-5level 日志級別從左顯示5個字符寬度
# %logger{50} 類名
# %msg%n 日志信息加換行
至于為什么 Spring Boot 的默認(rèn)日志輸出格式是這樣?
我們可以在 Spring Boot 的源碼里找到答案克胳。
自定義日志輸出
可以直接在配置文件編寫日志相關(guān)配置
# 日志配置
# 指定具體包(com.xdf)的日志級別
logging.level.com.xdf=debug
# 控制臺和日志文件輸出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level
%logger{50} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}
- %msg%n
# 日志輸出路徑平绩,默認(rèn)文件spring.log
logging.file.path=spring.log
#logging.file.name=log.log
關(guān)于日志的輸出路徑,可以使用 logging.file 或者 logging.path 進(jìn)行定義漠另,兩者存在關(guān)系如下表捏雌。
替換日志框架
因為 Log4j 日志框架已經(jīng)年久失修,原作者都覺得寫的不好笆搓,所以下面演示替換日志框架為 Log4j2 的 方式性湿。根據(jù)官網(wǎng)我們 Log4j2 與 logging 需要二選一,因此修改 pom如下
<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>