不論在工作中,亦或是求職面試,Spring Boot已經成為我們必知必會的技能項,如今的各行各業都在飛速的擁抱這個已經不是很新的Spring啟動架構。
當然,作為Spring Boot的精髓,自動配置原理的工作過程往往隻有在“面試”的時候才能用得上,但是如果在工作中你能夠深入的了解Spring Boot的自動配置原理,将無往不利。
Spring Boot的出現,得益于“習慣優于配置”的理念,沒有繁瑣的配置、難以內建的内容(大多數流行第三方技術都被內建),這是基于Spring 4.x提供的按條件配置Bean的能力。
自動配置原理
Spring Boot關于自動配置的源碼在spring-boot-autoconfigure-x.x.x.x.jar中:

當然,自動配置原理的相關描述,官方文檔貌似是沒有提及。不過我們不難猜出,Spring Boot的啟動類上有一個@SpringBootApplication注解,這個注解是Spring Boot項目必不可少的注解。那麼自動配置原理一定和這個注解有着千絲萬縷的聯系!
@EnableAutoConfiguration
@SpringBootApplication是一個複合注解或派生注解,在@SpringBootApplication中有一個注解@EnableAutoConfiguration,翻譯成人話就是開啟自動配置,其定義如下:
而這個注解也是一個派生注解,其中的關鍵功能由@Import提供,其導入的AutoConfigurationImportSelector的selectImports()方法通過SpringFactoriesLoader.loadFactoryNames()掃描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar裡就有一個這樣的spring.factories檔案。
這個spring.factories檔案也是一組一組的key=value的形式,其中一個key是EnableAutoConfiguration類的全類名,而它的value是一個xxxxAutoConfiguration的類名的清單,這些類名以逗号分隔,如下圖所示:
這個@EnableAutoConfiguration注解通過@SpringBootApplication被間接的标記在了Spring Boot的啟動類上。在SpringApplication.run(...)的内部就會執行selectImports()方法,找到所有JavaConfig自動配置類的全限定名對應的class,然後将所有自動配置類加載到Spring容器中。
自動配置生效
每一個XxxxAutoConfiguration自動配置類都是在某些條件之下才會生效的,這些條件的限制在Spring Boot中以注解的形式展現,常見的條件注解有如下幾項:
@ConditionalOnBean:當容器裡有指定的bean的條件下。
@ConditionalOnMissingBean:當容器裡不存在指定bean的條件下。
@ConditionalOnClass:當類路徑下有指定類的條件下。
@ConditionalOnMissingClass:當類路徑下不存在指定類的條件下。
@ConditionalOnProperty:指定的屬性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表當xxx.xxx為enable時條件的布爾值為true,如果沒有設定的情況下也為true。
以ServletWebServerFactoryAutoConfiguration配置類為例,解釋一下全局配置檔案中的屬性如何生效,比如:server.port=8081,是如何生效的(當然不配置也會有預設值,這個預設值來自于org.apache.catalina.startup.Tomcat)。
在ServletWebServerFactoryAutoConfiguration類上,有一個@EnableConfigurationProperties注解:開啟配置屬性,而它後面的參數是一個ServerProperties類,這就是習慣優于配置的最終落地點。
在這個類上,我們看到了一個非常熟悉的注解:@ConfigurationProperties,它的作用就是從配置檔案中綁定屬性到對應的bean上,而@EnableConfigurationProperties負責導入這個已經綁定了屬性的bean到spring容器中(見上面截圖)。那麼所有其他的和這個類相關的屬性都可以在全局配置檔案中定義,也就是說,真正“限制”我們可以在全局配置檔案中配置哪些屬性的類就是這些XxxxProperties類,它與配置檔案中定義的prefix關鍵字開頭的一組屬性是唯一對應的。
至此,我們大緻可以了解。在全局配置的屬性如:server.port等,通過@ConfigurationProperties注解,綁定到對應的XxxxProperties配置實體類上封裝為一個bean,然後再通過@EnableConfigurationProperties注解導入到Spring容器中。
而諸多的XxxxAutoConfiguration自動配置類,就是Spring容器的JavaConfig形式,作用就是為Spring 容器導入bean,而所有導入的bean所需要的屬性都通過xxxxProperties的bean來獲得。
可能到目前為止還是有所疑惑,但面試的時候,其實遠遠不需要回答的這麼具體,你隻需要這樣回答:
Spring Boot啟動的時候會通過@EnableAutoConfiguration注解找到META-INF/spring.factories配置檔案中的所有自動配置類,并對其進行加載,而這些自動配置類都是以AutoConfiguration結尾來命名的,它實際上就是一個JavaConfig形式的Spring容器配置類,它能通過以Properties結尾命名的類中取得在全局配置檔案中配置的屬性如:server.port,而XxxxProperties類是通過@ConfigurationProperties注解與全局配置檔案中對應的屬性進行綁定的。
什麼是starter
Starter可以了解為一個可拔插式的插件,提供一系列便利的依賴描述符,您可以獲得所需的所有Spring和相關技術的一站式服務。應用程式隻需要在maven中引入starter依賴,SpringBoot就能自動掃描到要加載的資訊并啟動相應的預設配置。用一句話描述,就是springboot的場景啟動器。
下面是Spring官方提供的部分starter,全部的請參考官網:
自定義starter
官方定義的starter命名都是spring-boot-starter-xxx,我們自己定義的一般都 xxx-spring-boot-starter,建立一個id-gengerate-spring-boot-starter項目
導入springboot 包spring-boot-starter
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--主要是為了生成spring-configration-metadata.json檔案,友善idea跳轉-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
1.建立配置類,定義一個映射配置新的的類IdProperties和LockieProperties,添加注解ConfigurationProperties("machine.id"),我們的配置檔案以machine.id開頭,比如mathine.id.name=boot-order,另外一個配置檔案是已spring.lockie開頭的配置檔案
package com.lockie.starter.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author: lockie
* @Date: 2020/11/3 15:10
* @Description: id生成配置類
*/
@Data
@ConfigurationProperties("machine.id")
public class IdProperties {
private String name;
}
package com.lockie.starter.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author: 鄒細良
* @Date: 2020/11/3 18:21
* @Description:
*/
@Data
@ConfigurationProperties("spring.lockie")
public class LockieProperties {
private String prefix;
private String suffix;
}
2.建立一個接口用來生成對應的業務,IdService裡面有個getId方法用來生成自定義的ID
package com.lockie.starter.service;
import com.lockie.starter.config.IdProperties;
import lombok.Data;
/**
* @author: lockie
* @Date: 2020/11/3 15:19
* @Description: 生成ID接口
*/
@Data
public class IdService {
private IdProperties idProperties;
/**
* 擷取ID
* @return
*/
public String getId() {
return idProperties.getName() + "-" + System.currentTimeMillis();
}
}
package com.lockie.starter.service;
import com.lockie.starter.config.LockieProperties;
import lombok.Data;
/**
* @author: lockie
* @Date: 2020/11/3 18:23
* @Description:
*/
@Data
public class LockieService {
LockieProperties lockieProperties;
public String hello(String name) {
return lockieProperties.getPrefix() + "-" + name + "-" + lockieProperties.getSuffix();
}
}
3.建立配置類,重要的配置參數
@Configuration:配置類
@EnableConfigurationProperties():開啟某屬性類,裡面封裝的是屬性資訊,相當于@ConfigurationProperties加載application.properties裡面的資訊,并用@Component将類交由spring管理
@ConditionalOnClass():當這個類存在時,再進行下面的步驟,否則就不進行下面的步驟了
@AutoConfigureAfter():當自動配置完成後,再執行裡面的類
@ConfigurationOnMissBean():當這個bean不存在時,再添加bean類
@Bean:添加bean類
/**
* @author: lockie
* @Date: 2020/11/3 15:17
* @Description: 擷取ID配置類
*/
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(IdProperties.class)
public class IdAutoConfiguation {
@Autowired
private IdProperties idProperties;
@Bean
public IdService idService() {
IdService idService = new IdService();
idService.setIdProperties(idProperties);
return idService;
}
}
/**
* @author: lockie
* @Date: 2020/11/3 18:26
* @Description:
*/
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties({LockieProperties.class})
public class LockieAutoConfiguration {
@Autowired
LockieProperties lockieProperties;
@Bean
public LockieService lockieService() {
LockieService lockieService = new LockieService();
lockieService.setLockieProperties(lockieProperties);
return lockieService;
}
}
4.最重要的一步,在resources目錄下建立META-INF目錄,并添加檔案spring.factories。在這個檔案中配置EnableAutoConfiguration,具體如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lockie.starter.config.IdAutoConfiguation,\
com.lockie.starter.config.LockieAutoConfiguration
測試自定義的Starter
我們在之前的項目boot-order-service中測試我們的starter, boot-order-service項目pom增加id-generate-spring-boot-starter包
<dependency> <groupId>com.lockie-starter</groupId> <artifactId>id-generate-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
項目包路徑一樣可能還需要在boot-order-starter啟動類上增加掃描id-generate-spring-boot-starter項目包路徑
@SpringBootApplication(scanBasePackages = "com.lockie.starter")
boot-order-service項目配置檔案增加配置參數
machine.id.name=boot-order spring.lockie.prefix=Hello! spring.lockie.suffix=Nice to meet you
到這裡我們在boot-order-service服務中就可以使用id-generate-spring-boot-starter項目中的services了,并且隻需要使用@Autowired即可
@SpringBootTest
@ContextConfiguration(classes = BootOrderServiceApplication.class)
class BootOrderServiceApplicationTests {
@Test
void contextLoads() {
}
@Autowired
IdService idService;
@Autowired
LockieService lockieService;
@Test
public void testIdGenerate() {
String id = idService.getId();
System.out.println("生成ID:" + id);
String str = lockieService.hello("小明");
System.out.println("打招呼:"+ str);
}
}
運作一下檢視結果,看到結果是我們想要的,至此我們自定義一個starter就成功了,總結一下就是要建立3個類1個配置檔案,xxProperties,xxService,xxAutoConfiguation和spring.factories