Spring Boot
“what?”, “how?” and “why?”
1. what?
1.1 什么是Spring Boot?
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.
Spring Boot使您可以輕松地創建獨立的、生產級的、基于Spring的應用程序,您可以“只是運行”。
我們對Spring平臺和第三方庫有一個獨到的見解,這樣您就可以從最少的麻煩開始了。大多數Spring引導應用程序需要很少的Spring配置。
Features
Create stand-alone Spring applications
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
Provide opinionated 'starter' dependencies to simplify your build configuration
Automatically configure Spring and 3rd party libraries whenever possible
Provide production-ready features such as metrics, health checks, and externalized configuration
Absolutely no code generation and no requirement for XML configuration
特征
創建獨立的Spring應用程序
直接嵌入Tomcat、Jetty或Undertow(不需要部署WAR文件)
提供自以為是的“starter”依賴項以簡化構建配置
盡可能自動配置Spring和第三方庫
提供生產就緒功能,如度量、運行狀況檢查和外部化配置
完全沒有代碼生成,也不需要XML配置
1.2 約定優于配置
約定由于配置(Convention over Configuration),又稱按約定編程,是一種設計范式。
本質上是說,系統、類庫或框架應該假定合理的默認值,而非要求提供不必要的配置。比如模型中有一個名為User的類,那么數據庫中對應的表名默命名user。只有在偏離這一約定的時候,例如想要將表名命名為person,才需要寫有關這個名字的配置。
比如項目架構師搭建項目就是限制軟件開發隨便寫代碼,在開發前,定制一套統一的規范,讓開發人員按照統一的要求進行開發、編碼、測試之類的,這樣就加強了開發效率和審查代碼效率。所以說寫代碼的時候就需要按照要求進行命名,這樣統一規范的代碼就有良好的可讀性和維護性了。
約定優于配置簡單來說就是遵循約定
2. how?
2.1 基礎功能
2.1.1 相關說明
功能 | 說明 |
---|---|
Maven | 3.3+ |
JDK | 1.8+ |
Spring Boot | 2.1.14.RELEASE |
Spring Framework | 5.1.15.RELEASE |
2.1.2 構建 Spring Boot 項目
2.1.2.1 pom文件說明
添加 web 環境依賴
<?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>springboot-learn</artifactId>
<version>1.0-SNAPSHOT</version>
<description>基于Spring Boot的案例</description>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.14.RELEASE</version>
</parent>
<dependencies>
<!-- spring boot web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.1.2.2 項目結構說明
2.1.2.3 創建Controller
// @RestController 為組合注解,等同于 Spring中的:@Controller + @ResponseBody
@RestController
public class LearnController {
@RequestMapping("/hello")
public String hello() {
return "你好 Spring Boot";
}
}
2.1.2.4 編寫啟動主程序類
@SpringBootApplication
public class LearnApplication {
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
問題:啟動主程序類的位置是否可以隨便放?
2.1.2.5 運行項目
運行項目主程序啟動類LearnApplication類,啟動成功后,可以看見默認端口為8080
頁面輸出為 “你好 Spring Boot”。
至此,構建Spring Boot項目就完成了。
2.1.3 單元測試
開發中,每當完成一個功能接口或業務方法編寫后,通常需要借助單元測試驗證功能是否正確。
Spring Boot對單元測試提供了很好的支持,在使用是,直接在pom.xml文件中添加
spring-boot-starter-test測試依賴啟動器,可以通過相關注解實現單元測試。
2.1.3.1 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.1.3.2 編寫測試代碼
// 測試啟動類,并加載Spring Boot測試注解
@RunWith(SpringRunner.class)
// 標記為Spring Boot單元測試類,并加載項目的ApplicationContext上下文環境
// classes 知道項目主程序啟動類
@SpringBootTest(classes = LearnApplication.class)
public class LearnApplicationTest {
@Autowired
private LearnController learnController;
@Test
public void test() {
String hello = learnController.hello();
System.out.println(hello);
}
}
2.1.4 熱部署
在開發過程中,通常會對一段業務代碼不斷的操作修改,在修改之后往往要重啟服務,有些服務需要加載很久才能啟動成功,這種不必要的重復操作極大的降低了程序開發效率。為此,Spring Boot框架專門提供了進行熱部署的依賴啟動器,用于進行項目熱部署,無需手動重啟項目。
2.1.4.1 添加依賴
<!-- 熱部署依賴啟動器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.1.4.2 IDEA工具設置
選擇IDEA工具界面【File】-【Settings】,打卡Compiler界面,勾選【Build project automatically】
點擊 Apply 應用,以及OK保存。
在項目任意界面使用組合快捷鍵【Ctrl+Alt+Shift+/】打開Maintenance選項框,選中并打開Registry頁面
列表中找到【compiler.automake.allow.when.app.running】,將該值Value進行勾選。用于指定IDEA工具在程序運行過程中自動編譯,最后單擊【Close】按鈕完成設置
2.1.4.3 效果測試
啟動項目,訪問 http://localhost:8080/hello
修改 “你好” 為 “hello”
為了測試熱部署的是否有效,接下來,在不關閉項目的情況下,將LearnController類中數據修改,并保存,查看控制臺會發現項目能夠自動構建和編譯,說明熱部署生效
2.1.5 全局配置文件
全局配置文件能夠對一些默認的配置值進行修改。Spring Boot使用一個application.properties或application.yml/application.yaml的文件作為全局配置文件,該文件放在 【src/main/resources】目錄或者類路徑的 【/config】,
一般為放在resources目錄。
我們可以在 application.properties / application.yml文件中定義Spring Boot定義項目的相關屬性,當然,這些屬性可以是系統屬性、環境變量、命令參數等信息,也可以是自定義配置文件名稱和位置。
# 指定項目運行端口
server.port=8080
# 指定項目應用上下文路徑
server.servlet.context-path=/learn
# 指定項目名
spring.application.name=learn
2.1.5.1 配置自動提示
編寫配置時,由于我們配置的Person對象屬性是我們自定義的,Spring Boot 無法自動識別,所有不會有任何屬性提示。在實際開發中,為了出現代碼提示的效果方便配置,在使用@ConfigurationProperties注解進行配置文件屬性注入時,可以在pom.xml文件中添加Spring Boot提供的配置處理依賴器。
<!-- 配置處理器依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
添加配置后,需要重新編譯、運行項目即可生效。
2.1.5.2 演示準備
首先準備兩個實體類:Pet、Person,下面通過application.properties和application.yml配置文件中的自定義配置屬性注入到Person實體類中
@Data
public class Pet {
private String type;
private String name;
}
@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int id; // id
private String name; // 名稱
private List hobby; // 愛好
private String[] family; // 家庭成員
private Map map;
private Pet pet; // 寵物
}
@ConfigurationProperties(prefix = "person")注解的作用是將配置文件中以person開頭的屬性值通過setXXX()方法注入到實體類中
@Component注解的作用是將當前注入屬性值的Person類對象作為Bean組件放到Spring容器中,只有這樣才能被@ConfigurationProperties賦值
2.1.5.3 application.properties
person.id=1
person.name=張三
person.hobby=吃飯,睡覺,打豆豆
person.family=father,mother
person.map.k1=v1
person.map.k3=v2
person.pet.type=cat
person.pet.name=麻花
測試類
// 測試啟動類,并加載Spring Boot測試注解
@RunWith(SpringRunner.class)
// 標記為Spring Boot單元測試類,并加載項目的ApplicationContext上下文環境
// classes 知道項目主程序啟動類
@SpringBootTest(classes = LearnApplication.class)
public class LearnApplicationTest {
@Autowired
private Person person;
@Test
public void testProperties() {
System.out.println(person);
}
}
打印結果
Person(id=1, name=張三, hobby=[吃飯, 睡覺, 打豆豆], family=[father, mother], map={k1=v1, k3=v2}, pet=Pet(type=cat, name=麻花))
可以看出,正確打印出了Person類對象,說明application.properties配置文件屬性配置正確,并通過相關注解自動完成了屬性注入。
2.1.5.4 application.yaml
yaml文件格式是Spring Boot支持的一種JSON超集文件格式,相比傳統的properties配置文件,yaml文件以數據為核心,是一種更為直觀且容易被電腦識別的數據序列化格式。application.yaml配置文件的工作原理和application.properties是一樣的,只不過yaml格式配置文件看起來更加簡潔一些。
- yaml文件看擴展名可以為 .yaml或 .yml
- application.yml文件使用 “key:(空格)value” 格式配置屬性,使用縮進控制層級關系
針對不同的數據類型有不同的格式
-
value值為普通數據類型(數字,字符串、布爾等)
server: port: 8081 spring: application: name: demo
-
value值為數組和單列集合
主要有兩種書寫方式:縮進式寫法和行內寫法
-
縮進式寫法
person: hobby: - 吃飯 - 睡覺 - 打豆豆
或
person: hobby: 吃飯, 睡覺, 打豆豆
-
行內寫法
person: hobby: [吃飯,睡覺,打豆豆]
或
person: hobby: 吃飯,睡覺,打豆豆
-
-
value值為Map集合和對象
主要有兩種書寫方式:縮進式寫法和行內寫法
-
縮進式寫法
person: map: k1: v1 k2: v2
-
行內式寫法
person: map: {k1: v1-1,k2: v2-2}
-
測試
在resources下創建 application.yml
person:
id: 1
name: 羅杰
hobby: [吃飯,睡覺,打豆豆]
family: father,mother
map: {k1: v1-1,k2: v2-2}
pet:
type: cat
name: 麻花
測試類
// 測試啟動類,并加載Spring Boot測試注解
@RunWith(SpringRunner.class)
// 標記為Spring Boot單元測試類,并加載項目的ApplicationContext上下文環境
// classes 知道項目主程序啟動類
@SpringBootTest(classes = LearnApplication.class)
public class LearnApplicationTest {
@Autowired
private Person person;
@Test
public void testProperties() {
System.out.println(person);
}
}
打印結果
Person(id=1, name=張三, hobby=[吃飯, 睡覺, 打豆豆], family=[father, mother], map={k1=v1, k3=v2}, pet=Pet(type=cat, name=麻花))
可以看出,正確打印出了Person類對象,說明application.yml配置文件屬性配置正確,并通過相關注解自動完成了屬性注入。
2.1.5.5 配置文件屬性值注入
使用Spring Boot進行全局配置文件設置時:
- 如果配置的屬性是Spring Boot已有屬性,例如服務端口server.port,那么Spring Boot內部會自動掃描并讀取這些配置文件中的屬性值并默認覆蓋。
- 如果配置的屬性是用戶自定義屬性,例如剛剛自定義的Person實體類屬性,還必須在程序中注入這些配置屬性方可生效。
Spring Boot支持多種注入配置文件屬性的方式,@ConfigurationProperties和@Value方式
2.1.5.5.1 @ConfigurationProperties
Spring Boot 提供 @ConfigurationProperties 注解用來快速、方便的將配置文件中自定義的屬性批量注入到某個Bean對象的多個屬性中。
如
@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int id; // id
private String name; // 名稱
private List hobby; // 愛好
private String[] family; // 家庭成員
private Map map;
private Pet pet; // 寵物
}
上述代碼使用 @Component 和 @ConfigurationProperties(prefix = "person")將配置文件中的每個屬性映射到Person類組件中。
2.1.5.5.2 @Value
@Value是 Spring 框架提供的,用來讀取配置文件中的屬性值,并逐個注入到Bean對象的對應屬性中,Spring Boot從Spring框架中對@Value注解進行了默認繼承,所以在Spring Boot框架中還可以使用該注解讀取和注入配置文件屬性值。
如
@Data
@Component
public class People {
@Value("${people.id}")
private Integer id;
@Value("${people.name}")
private String name;
}
people:
id: 1
name: 李四
測試結果
People(id=1, name=李四)
可以看出,屬性值正確打印,通過@Value可以進行配置文件屬性值進行注入。
使用@Value注解使用注意事項
-
如果使用@Value,在配置文件中配置對應屬性或者設置默認值,否則會出現異常
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'people.name' in value "${people.name}"
默認值設置方式示例
@Data @Component public class People { @Value("${people.id:10}") private Integer id; @Value("${people.flag:true}") private Boolean flag; // 設置默認值為空字符串 @Value("${people.name:}") private String name; // 設置默認值為null @Value("${people.remark:#{null}}") private String remark; }
-
@Value注解對Map集合、對象以及yml文件格式的行內式寫法的配置文件的屬性注入都不支持,如果賦值會出現錯誤
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'people.name' in value "${people.name}"
2.1.6 自定義配置
Spring Boot免除了項目中大部分的手動配置,對于一些特定情況,我們可以通過修改全局配置文件以適應具體生成環境,可以說,幾乎所有的配置都可以寫在application.properties配置文件中,Spring Boot會自動加載全局配置文件從而免除我們手動加載的煩惱。
但是,如果我們自定義配置文件,Spring Boot是無法識別這些配置文件的,此時需要我們手動加載。
2.1.6.1 @PropertySource
對應這種加載自定義配置文件的需求,可以使用@PropertySource注解結合@Component注解的方式來實現。
@PropertySource注解作用是用于指定自定義配置文件的具體位置和名稱,同時,為了保證Spring Boot能夠掃描該注解,還需要在其類上添加@Component,表明該類交于Spring容器進行維護。
當然對于自定義配置文件中的屬性值注入到對應的類屬性值中,可以使用@ConfigurationProperties或者@Value注解進行屬性值注入
示例
-
創建test.properties
test.id=1 test.name=zhangsan
-
創建配置類
@Data @Component // 引入自定義配置文件的名稱和位置 @PropertySource(value = "classpath:test.properties", encoding = "UTF-8") @ConfigurationProperties(prefix = "test") public class MyProperties { private int id; private String name; }
-
測試
// 測試啟動器,并加載Spring boot 測試注解 @RunWith(SpringRunner.class) // 標記該類為Spring boot 單元測試類,并加載項目的ApplicationContext上下文環境 @SpringBootTest class SpringbootDemoApplicationTests { @Autowired private MyProperties myProperties; @Test void contextLoads() { System.out.println(myProperties); } }
2.1.6.2 @Configuration
在Spring Boot框架中,推薦使用配置類的方式向容器中配置和組件
在Spring Boot框架中,通常使用 @Configuration 注解定義一個配置類,Spring Boot 會自動掃描和識別配置類,從而替換傳統Spring框架中的XML配置文件。
當定義一個配置類后,還需要在類中的方法上使用@Bean注解進行組件配置,將方法的返回對象注入到Spring容器中,并且組件名稱默認為方法名,當然也可以使用@Bean注解的name、value屬性自定義組件的名稱。
-
創建Config配置類
// 標明該類為配置類 @Configuration public class MyConfig { @Bean // 將返回值對象作為組件,添加到Spring容器中,標識id默認為方法名或者自定義@Bean(id) public MyService myService() { return new MyService(); } }
-
測試
// 測試啟動器,并加載Spring boot 測試注解 @RunWith(SpringRunner.class) // 標記該類為Spring boot 單元測試類,并加載項目的ApplicationContext上下文環境 @SpringBootTest class SpringbootDemoApplicationTests { @Autowired private ApplicationContext applicationContext; @Test void contextLoads() { boolean myService = applicationContext.containsBean("myService"); System.out.println(myService); } }
2.1.7 隨機數設置
在Spring Boot配置文件中,隨機值設置使用到了Spring Boot內嵌的RandomValuePropertySource類,對一些隱秘屬性值或者測試用例屬性值進行隨機值注入
隨機值設置的語法格式為 ${random.xx},xx標識需要指定生成的隨機數類型和范圍,可以是整數,uuid,或者字符串
@Data
@Component
@ConfigurationProperties(prefix = "myr")
public class MyRandom {
private String secret; // 配置隨機值
private Integer number; // 配置隨機整數
private Long bignumber; // 配置隨機Long類型整數
private String uuid; // 配置隨機uuid
private int lessthanten; // 配置小于10的隨機整數
private int range; // 配置范圍在[1024,5048]之間的隨機整數
}
myr:
secret: ${random.value} # 配置隨機值
number: ${random.int} # 配置隨機整數
bignumber: ${random.long} # 配置隨機Long類型整數
uuid: ${random.uuid} # 配置隨機uuid
lessthanten: ${random.int(10)} # 配置小于10的隨機整數
range: ${random.int[1024,5048]} # 配置范圍在[1024,5048]之間的隨機整數
2.1.8 參數間引用
在Spring Boot配置文件中,配置文件的 屬性值還可以進行參數間的引用,也就是在后一個配置的屬性值中引用先前已經定義多的屬性,這樣就可以直接解析其中的屬性值了。
參數間引用的語法格式:${xxx},xxx表示先前在配置文件中已經配置過的屬性名
好處:多處引用,一處配置
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class App {
private String name;
private String describe;
}
app:
name: 測試APP
describe: ${app.name}是用來測試的
2.1.9 Profiles
在項目的開發中,有些配置文件在不同的環境(開發、測試、生成)中配置信息是不同的,例如數據庫連接信息、Redis配置信息、日志控制級別等等都是不同的,那么就需要我們再項目中根據不同的環境配置不同的配置信息,做到不同的環境配置不同的配置,做到各個環境配置隔離。
在 Spring Boot 中多環境配置文件名需要使用 【application-{profile}.properties】或
【application-{profile}.yml】的格式,這里 {profile} 對應的是不同的環境標識。
如
application-test.properties 或 application-test.yml
application-prod.properties 或 application-prod.yml
需求:
測試環境服務端口:8081,生產環境端口:8082,如何進行實現
-
創建對應環境的配置文件
application-test.yml 和 application-prod.yml
application-test.yml配置文件內容:
server: port: 8081
application-prod.yml配置文件內容:
server:
port: 8082
-
application.yml激活對應環境配置
spring: profiles: active: prod
-
測試
java -jar learn.jar --spring.profiles.active=test
不同的環境啟動腳本中激活不同的配置即可。
2.1.10 常用注解
注解 | 說明 |
---|---|
@SpringBootApplication | 組合注解:等價于@Configuration、@EnableAutoConfiguration、@ComponentScan |
@EnableAutoConfiguration | 啟用自動配置功能 |
@ComponentScan | 組件掃描,可自動發現和裝配一些Bean |
@Import | 導入其他配置類 |
@Bean | 相當于Spring中xml中的bean標簽 |
@Configuration | 標記該類為配置類,等價于Spring中xml配置文件 |
@ConditionalOnMissingBean | 未在類路徑下中找到對應Bean執行 |
@ConditionalOnBean | 在類路徑下中找到對應Bean執行 |
@ConditionalOnProperty | 在全局配置文件中找到對應屬性執行 |
@ConditionalOnMissingClass | 未在類路徑下中找到對應的Class執行 |
@ConditionalOnClass | 在類路徑下中找到對應的Class執行 |
@RestController | 組合注解,等價于@Controller、@ResponseBody |
@Autowired | Spring 依賴注入Bean,基于類型byType |
@Qualifier | 當容器中有多個同類型的Bean,通過該注解來指定,與@Autowired結合使用 |
@Service | 業務類 |
@Component | 組件類 |
@Repository | 數據訪問層 |
2.1.11 優雅關閉
當線上某個應用需要升級部署時,常常簡單粗暴地使用 kill 命令,這種停止應用的方式會讓應用將所有處理中的請求丟棄,響應失敗。這樣的響應失敗尤其是在處理重要業務邏輯時需要極力避免的,那么久需要平滑的關閉。
Spring Boot 框架提供健康監控依賴啟動器,可以對Spring Boot服務進行監控,優雅停服等功能
2.1.11.1 引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.1.11.2 添加配置
# 開啟Spring Boot優雅關閉
management.endpoint.shutdown.enabled=true
# 暴露shutdown端點
management.endpoints.web.exposure.include=shutdown
# 自定義管理端點的前綴(保證安全)
management.endpoints.web.base-path=/me-actuator
# 自定義actuator端口
management.server.port=12581
# 不允許遠程管理連接(不允許外部調用保證安全)
management.server.address=127.0.0.1
2.1.11.3 測試
執行 kill -9 / 或者 Ctrl+C操作,或者請求接口
http://localhost:12581/me-actuator/shutdown
返回數據
{"message":"Shutting down, bye..."}
說明優雅關閉成功
2.1.12 定時任務
Spring 框架自帶任務調度功能,好比一個輕量級的Quartz,使用簡單、方便,不需要依賴其他JAR包。
只需要在項目主程序啟動類上添加@EnableScheduling開啟任務調度功能即可
@SpringBootApplication
@EnableScheduling
public class LearnApplication {
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
2.1.12.1 簡單定時任務
@Component
public class TestTask {
@Scheduled(cron = "0/10 * * * * *")
public void testTask1() {
System.out.println("【任務一】測試定時任務" + LocalDateTime.now());
}
}
如上述,配置一個簡單的定時任務只需要在調度方法上添加@Shceduled注解即可,就可以使用定時任務。
2.1.12.2 異步定時任務
@Component
// 開啟異步支持
@EnableAsync
public class TestTask {
@Scheduled(cron = "0/10 * * * * *")
// 方法使用異步執行,每次任務創建一個線程執行任務
@Async
public void testTask1() {
System.out.println("【任務一】測試定時任務" + LocalDateTime.now() + " " + Thread.currentThread().getName());
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【任務一】休眠" + (i + 1) + "秒測試定時任務" + LocalDateTime.now() + " " + Thread.currentThread().getName());
}
}
}
往往在我們的項目調度任務中,有的場景是需要在當前任務還沒有執行完畢時,就需要執行下一個定時調度任務,在這種情況下需要使用異步的方式來執行定時任務。
@EnableAsync開啟異步支持
@Async標記任務使用異步執行(下次任務將在下一個配置時間開始,不等待當前任務執行完畢)
2.1.12.3 動態定時任務
當我們編寫定時任務是,流程大致為:編碼->配置執行周期->啟動服務。
當前我們配置的執行周期是每天早上8點執行,當我們有天,需求變更,需要每天晚上8點執行,我們的操作流程為:修改執行周期->新版打包->停服->啟動新版服務。整個流程線步驟多,存在不可控因素。
那么我們怎么做到不停服更新我們的執行周期呢??
那么下面我們模擬將cron表達式存儲在MySQL。
1)定義cron相關service
// 表達式相關接口
public interface SwitchService {
/**
* 獲取最新 cron 表達式
*
* @param taskId 任務ID
* @return 最新 cron表達式
*/
String getCron(String taskId);
/**
* 修改 cron 表達式
*/
void modify();
}
// 表達式相關接口實現
@Service
public class SwitchServiceImpl implements SwitchService {
private static String DB_CRON = "";
@Override
public String getCron(String taskId) {
System.out.println("執行數據庫查詢 DB_CRON " + LocalDateTime.now());
return DB_CRON;
}
@Override
public void modify() {
DB_CRON = "0/20 * * * * *";
System.out.println("修改數據庫中 DB_CRON " + LocalDateTime.now());
}
}
此處模擬修改以及查詢
2)創建具體任務執行
@Component
public class DynamicCronTask implements SchedulingConfigurer {
// 模擬當前任務ID
private String TASK_ID = "5001";
@Autowired
private SwitchService switchService;
private String SpringDynamicCronTask() {
// 默認為 每5秒執行
String cron = "0/5 * * * * ?";
//從數據庫獲得配置的corn表達式
String dbCron = switchService.getCron(TASK_ID);
// 當查詢為空時,使用默認的表達式
if (StringUtils.isNotBlank(dbCron)) {
return dbCron;
}
return cron;
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
// 任務邏輯
System.out.println("執行任務邏輯...." + LocalDateTime.now());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String s = SpringDynamicCronTask();
// 任務觸發,可修改任務的執行周期
CronTrigger trigger = new CronTrigger(s);
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
});
}
}
3)啟動服務
查看執行日志
執行數據庫查詢 DB_CRON 2020-06-09T10:33:30.001
執行任務邏輯....2020-06-09T10:33:35.002
執行數據庫查詢 DB_CRON 2020-06-09T10:33:35.002
執行任務邏輯....2020-06-09T10:33:40.001
執行數據庫查詢 DB_CRON 2020-06-09T10:33:40.001
修改數據庫中 DB_CRON 2020-06-09T10:33:42.085
執行任務邏輯....2020-06-09T10:33:45
執行數據庫查詢 DB_CRON 2020-06-09T10:33:45
執行任務邏輯....2020-06-09T10:34:00.001
執行數據庫查詢 DB_CRON 2020-06-09T10:34:00.001
執行任務邏輯....2020-06-09T10:34:20.002
執行數據庫查詢 DB_CRON 2020-06-09T10:34:20.002
通過日志可以看出,在應用啟動時,會首先從數據庫中查詢配置的執行周期,然后執行定時任務,執行完畢后會再次查詢執行周期,下一個執行時間結束后就會按照修改的執行時間執行。
生效時間為下一個執行時間結束后,做不到立即生效!!!
2.2 原理深入
傳統的Spring框架實現一個WEB服務,需要導入各種依賴JAR包,然后編寫對應的XML配置文件等,相比較而言,Spring Boot 顯得更加方便、快捷、高效。那么Spring Boot 究竟是如何做到這些的呢?
2.2.1 依賴管理
問題:為什么Spring Boot導入dependency時不需要指定版本?
2.2.1.1 spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.14.RELEASE</version>
</parent>
從上述可以看出,將spring-boot-starter-parent依賴作為Spring Boot項目的統一父項目依賴管理,并將項目版本號統一為2.1.14.RELEASE,該版本號可根據實際開發進行修改。
進入spring-boot-starter-parent底層源文件,發現spring-boot-starter-parent的底層有一個父依賴spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.14.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
繼續進入spring-boot-dependencies底層源文件,其核心代碼如下
<properties>
......
<jolokia.version>1.6.2</jolokia.version>
<jooq.version>3.11.12</jooq.version>
<jsonassert.version>1.5.0</jsonassert.version>
<json-path.version>2.4.0</json-path.version>
<jstl.version>1.2</jstl.version>
<jtds.version>1.3.1</jtds.version>
<junit.version>4.12</junit.version>
<junit-jupiter.version>5.3.2</junit-jupiter.version>
<kafka.version>2.0.1</kafka.version>
<kotlin.version>1.2.71</kotlin.version>
<lettuce.version>5.1.8.RELEASE</lettuce.version>
<liquibase.version>3.6.3</liquibase.version>
<log4j2.version>2.11.2</log4j2.version>
<logback.version>1.2.3</logback.version>
<lombok.version>1.18.12</lombok.version>
......
<spring.version>5.1.15.RELEASE</spring.version>
<spring-amqp.version>2.1.14.RELEASE</spring-amqp.version>
<spring-batch.version>4.1.4.RELEASE</spring-batch.version>
<spring-cloud-connectors.version>2.0.7.RELEASE</spring-cloud-connectors.version>
<spring-data-releasetrain.version>Lovelace-SR17</spring-data-releasetrain.version>
<spring-framework.version>${spring.version}</spring-framework.version>
<spring-hateoas.version>0.25.2.RELEASE</spring-hateoas.version>
<spring-integration.version>5.1.10.RELEASE</spring-integration.version>
<spring-kafka.version>2.2.13.RELEASE</spring-kafka.version>
<spring-ldap.version>2.3.3.RELEASE</spring-ldap.version>
<spring-plugin.version>1.2.0.RELEASE</spring-plugin.version>
<spring-restdocs.version>2.0.4.RELEASE</spring-restdocs.version>
<spring-retry.version>1.2.5.RELEASE</spring-retry.version>
<spring-security.version>5.1.10.RELEASE</spring-security.version>
<spring-session-bom.version>Bean-SR10</spring-session-bom.version>
<spring-ws.version>3.0.9.RELEASE</spring-ws.version>
<sqlite-jdbc.version>3.25.2</sqlite-jdbc.version>
......
</properties>
從底層spring-boot-dependencies底層源文件可以看出,該文件通過標簽對一些常用技術框架的依賴文件進行了統一版本號管理,例如activemq、spring、lombok等,都有與2.1.14.RELEASE版本相匹配的版本,這也就是pom.xml引入依賴文件不需要標注依賴jar版本號的原因。
問題:spring-boot-starter-parent父依賴啟動器的主要作用是進行版本統一管理,那么項目運行依賴的JAR包是從何而來?
2.2.1.2 spring-boot-starter-web
查看 spring-boot-starter-web源文件源碼
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.14.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.1.14.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.14.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.19.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.15.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.15.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
從上述代碼可以看出,spring-boot-starter-web依賴啟動器主要作用是提供了web開發場景需要的底層所有依賴。
正是因為如此,在pom.xml中引入 spring-boot-starter-web 依賴啟動器時,就可以實現web場景開發,而不需要額外導入Tomcat服務器依賴以及其他web依賴文件。
當然,這些引入依賴的文件版本還是由 spring-boot-starter-parent進行統一管理。
2.2.1.3 starter
Spring Boot除了提供上述web依賴啟動器以外,還提供了許多場景所需要的依賴
列出的是Spring Boot提供的一部分啟動依賴器,還有許多,可以從spring官網上查看。不同的場景依賴,我們可以根據我們不同的業務場景,直接在pom.xml中引入即可。
但是Spring Boot官網并不是針對所有的場景的開發技術框架都提供了依賴啟動器,如mybatis、druid等,但是為了充分利用Spring Boot框架的優勢,mybatis、druid等技術框架團隊主動與Spring Boot框架進行了整合,實現了各自的依賴啟動器。mybatis-spring-boot-starter、druid-spring-boot-starter。在需要的時候直接在pom.xml文件中導入即可,但是需要自己管理版本號。
2.2.2 自動配置
能夠在我們添加jar包依賴時,自動為我們進行配置一下配置,我們可以不需要配置或者少量配置就能運行編寫的項目。
問題:Spring Boot到底是如何進行自動配置的,都把那些組件進行了自動配置?
2.2.2.1 @SpringBootApplication
Spring Boot 應用啟動的入口是@SpringBootApplication注解標注類的main方法,
@SpringBootApplication能夠掃描Spring組件并且自動配置Spring Boot
@SpringBootApplication
public class LearnApplication {
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
@SpringBootApplication注解類
// 注解的適用范圍:類、接口、枚舉
@Target({ElementType.TYPE})
// 注解的生命周期:運行時
@Retention(RetentionPolicy.RUNTIME)
// 標明注解可標注在javadoc中
@Documented
// 標明注解可以被子類繼承
@Inherited
// 標明該類為配置類
@SpringBootConfiguration
// 啟動自動配置功能
@EnableAutoConfiguration
// 包掃描器
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
從上面可以看出,@SpringBootApplication注解主要由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan這三個核心注解組成。
2.2.2.1.1 @SpringBootConfiguration
@SpringBootConfiguration 注解標明其類為Spring Boot配置類
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 配置到IOC容器
@Configuration
public @interface SpringBootConfiguration {
}
從上述可以看出,@SpringBootConfiguration注解類主要注解為@Configuration注解,該注解由Spring框架提供,表示當前類為一個配置類,并且可以被組件掃描器掃描。
2.2.2.1.2 @EnableAutoConfiguration
@EnableAutoConfiguration注解表示為自動配置類,該注解是Spring Boot最重要的注解,也是實現自動配置的注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自動配置包
@AutoConfigurationPackage
// 自動配置掃描導入
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
從源碼可以發現,@EnableAutoConfiguration注解為一個組合注解,其作用就是借助@Import注解導入特定場景需要向IOC注冊的Bean,并且加載到IOC容器。@AutoConfigurationPackage就是借助@Import來搜集所有符合自動配置條件的Bean定義,并且加載到IOC容器中。
-
@AutoConfigurationPackage
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited // 導入Registrar中注冊的組件 @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
從上述源碼中可以看出@AutoConfigurationPackage注解的功能是有@Import注解實現的。@Import它是Spring框架底層注解,它的作用就是給容器導入某個組件類
Registrar類源碼
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 將主程序類所在的包以及所有子包下的組件掃描到Spring容器
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
從上述可以看出,@AutoConfigurationPackage注解的主要作用就是將主程序類所在的包以及所有子包下的組件加載到IOC容器中。
因此:在定義項目包目錄時,要求定義的包結構必須規范,項目主程序啟動類要放在最外層的根目錄位置,然后在根目錄的位置內部建立子包和類進行業務開發,這樣才能保證定義的類才能被組件掃描器掃描。
-
@Import({AutoConfigurationImportSelector.class})
將AutoConfigurationImportSelector類導入到Spring容器中。
AutoConfigurationImportSelector可以幫助 Spring Boot 應用將所有符合條件@Configuration的配置都導入到當前Spring Boot創建并使用的IOC容器(ApplicationContext)中。
// 自動配置的過程 public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 獲取自動配置的配置類 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } // 獲取自動配置元信息 private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata == null) { // 加載自動配置元信息,需要傳入beanClassLoader這個類加載器 this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); } return this.autoConfigurationMetadata; } // 獲取自動配置的配置類 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // 從META-INF/spring.factories配置文件中將對于的自動配置類獲取到 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
深入AutoConfigurationMetadataLoader.loadMetadata()方法
// 文件中需要加載的配置類的類路徑
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
// 讀取spring-boot-autoconfigure-2.1.14.RELEASE.jar中spring-autoconfigure-metadata.properties的信息生成URL
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
深入**AutoConfigurationImportSelector.getCandidateConfigurations() **方法
這個方法有一個重要的loadFactoryNames方法,這個方法讓SpringFactoriesLoader去加載一些組件的名字。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 這個方法需要兩個參數,getSpringFactoriesLoaderFactoryClass()、getBeanClassLoader()
// getSpringFactoriesLoaderFactoryClass() 返回的:EnableAutoConfiguration.class
// getBeanClassLoader() 返回的:beanClassLoader類加載器
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
繼續深入loadFactoryNames()方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 獲取出的健
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 如果類加載器不為空,這加載類路徑下的META-INF/spring.factories,將其中設置的配置類的類路徑信息封裝為Enumeration對象
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories"));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
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());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
會去讀取一個 spring.factories 的文件,讀取不到會報錯
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
它其實是去加載一個外部的文件,而這個文件是在
@EnableAutoConfiguration 注解就是從classpath中搜尋META-INF/spring.factories配置文件,并將其org.springframework.boot.autoconfigure.EnableAutoConfiguration對于的配置通過反射實例化對應的標注了@Configuration的JavaConfig配置類,并且加載到IOC容器中。
以web項目為例,在項目中加入了web環境依賴啟動器,對應的org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration自動配置就會生效,打開自動配置就會發現,在配置類中通過全注解的方式對 Spring MVC 運行環境所需要環境進行了默認配置,包括前綴、后綴、試圖解析器、MVC校驗器等。
總結
Spring Boot 底層自動配置的步驟:
1)Spring Boot 應用啟動
2)@SpringBootApplication 注解起作用
3)@EnableAutoConfiguration 注解起作用
4)@AutoConfigurationPackage 注解起作用
@AutoConfigurationPackage 這個注解主要作用就是@Import({AutoConfigurationPackages.Registrar.class}),它通過Registrar類導入容器中,而Registrar的作用就是將掃描主配置類的包以及子包,并將對應的組件導入IOC容器中。
5)@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class) 它將 AutoConfigurationImportSelector 類導入容器中,AutoConfigurationImportSelector 類的作用是通過getAutoConfigurationEntry()方法執行的過程中,會使用內部工具類SpringFactoriesLoader,查找classpath上所有的jar包中的META-INF/spring.factories進行加載,實現將配置類信息交給Spring Factory加載器進行一系列的容器創建過程。
2.2.2.1.3 @ComponentScan
@ComponentScan 注解具體掃描包的路徑,由Spring Boot主程序所在包的位置決定。在掃描的過程中由@AutoConfigurationPackage注解進行解析,從而得到Spring Boot主程序類所在包的具體位置
2.2.3 自定義starter
Spring Boot由眾多Starter組成(一系列的自動化配置的starter插件),Spring Boot之所以流行,也是因為starter。
starter是 Spring Boot非常重要的一部分,可以理解為一個可以插拔的插件,正是因為這些starter使得使用某個功能的開發者不需要關注各種依賴庫的處理,不需要具體的配置信息,由 Spring Boot 自動通過classpath路徑下的類發現需要的Bean,并織入對應的Bean。
例如,我們需要 Redis 插件,那么可以使用 spring-boot-starter-data-redis,如果需要MongoDB,那么可以使用spring-boot-starter-data-mongodb
2.2.3.1 為什么需要自定義starter
開發過程中,經常會有一些獨立于業務之外的配置模塊。如果我們將這些可以獨立業務代碼之外的功能配置封裝成一個個的starter,復用的時候只需要將其在pom.xml中引用依賴即可,由Spring Boot幫我們完成自動裝配。
2.2.3.2 自定義starter命名規則
Spring Boot提供的starter以 spring-boot-starter-xxxx 的方式命名的,官方建議自定義的starter使用
xxxx-spring-boot-starter 命名規則,以區分Spring Boot生態提供的starter。
2.2.3.3 自定義
2.2.3.3.1 custom-spring-boot-starter
-
新建Maven工程,工程命名custom-spring-boot-starter,導入依賴
<?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>custom-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.1.14.RELEASE</version> </dependency> </dependencies> </project>
-
編寫JavaBean
@EnableConfigurationProperties(CustomBean.class) @ConfigurationProperties(prefix = "custom") public class CustomBean { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
編寫配置類
@Configuration // 當類路徑classpath下有指定的類的情況下進行自動配置 @ConditionalOnClass public class CustomConfig { @Bean public CustomBean customBean() { return new CustomBean(); } }
-
resources下創建META-INF/spring.factories
注意:META-INF 目錄和 spring.factories文件 需要手動創建
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
site.luojie.custom.config.CustomConfig
2.2.3.3.2 使用custom-spring-boot-starter
-
導入自定義starter依賴
<dependency> <groupId>org.example</groupId> <artifactId>custom-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
編寫配置文件
custom.id=1 custom.name=測試
-
單元測試
@RunWith(SpringRunner.class) @SpringBootTest public class LearnApplicationTest { @Autowired private CustomBean customBean; @Test public void testCustomBean(){ System.out.println(customBean); } }
3. why?
3.1 為什么選擇Spring Boot?
3.1.1 Spring MVC 優缺點
優點:
Spring是Java企業級(Java Enterprise Edition,JEE,也稱 J2EE)的輕量級代替品。無需開發重量級的 Enterprise JavaBean(EJB),Spring作為企業級Java開發提供了一種相對簡單的方法,通過依賴注入和面向切面編程,用簡單的 Java 對象(Plain Old Java Object,POJO)實現了EJB功能。
Spring核心:IOC、AOP
缺點:
雖然Spring的組件代碼是輕量級的,但它的配置確實重量級的。一開始,Spring用XML配置,而且是很多的XML配置。Spring 2.5引入了基于注解的組件掃描,這消除了大量針對應用程序自身組件顯示XML配置。Spring3.0引入了基于Java的配置,這是一種安全的可重構配置方式,可以代替XML。
所以這些配置代表了開發時的耗時。因為在思考Spring特性配置和解決業務問題之間需要進行思維切換,所以編寫配置擠占了編寫應用程序邏輯的時間。和所有框架一樣,Spring實用,但與此同時它要求的回報也不少。
除此之外,項目的依賴管理也是一件耗時耗力的事情。在搭建環境時,需要分析要導入那些庫的坐標,而且還需要分析導入與之有依賴關系的其他庫的坐標,一旦選錯了依賴版本,隨之而來的不兼容問題就會嚴重阻礙項目的開發進度。
- 重量級配置、配置繁瑣
- 項目整合耗時耗力
3.1.2 Spring Boot 如何解決?
底部依賴
整合相關 starter。
常用starter
spring-boot-starter-web
spring-boot-starter-test
spring-boot-starter-data-redis
spring-boot-starter-tomcat
mybatis-spring-boot-starter
druid-spring-boot-starter
自動配置
自動注冊相關Bean到IOC容器中,需要用時直接使用@Autowired
簡單、快速、方便搭建項目,對主流開發框架無配置集成,極大提高了開發、部署效率