spring boot最核心的特性就是他的自動化配置特性,極大的減少了構建一個spring web工程的工作量。那麼你知道spring boot自動化配置的原理嗎?
先直接自定義一個user-spring-boot-starter組件,感受下自動化配置的魅力。
構建user-spring-boot-starter
pom依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.laowan</groupId>
<artifactId>user-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-spring-boot-starter</name>
<description>user-spring-boot-starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
添加配置屬性
spring.user.enabled=false
spring.user.name=laowan
創建UserPorperties屬性類使用配置屬性
@Data
@ConfigurationProperties("spring.user")
public class UserPorperties {
private String name;
}
利用屬性類UserPorperties構建UserClient對象
public class UserClient {
private UserPorperties userPorperties;
public UserClient(){
}
public UserClient(UserPorperties userPorperties){
this.userPorperties = userPorperties;
}
public String getName(){
return userPorperties.getName();
}
}
核心:創建UserAutoConfigure自動化配置類
@Configuration
@EnableConfigurationProperties(UserPorperties.class)
public class UserAutoConfigure {
@Bean
@ConditionalOnProperty(prefix = "spring.user",value ="enabled",havingValue = "true")
public UserClient userClient(UserPorperties userPorperties){
return new UserClient(userPorperties);
}
}
說明:
@ConfigurationProperties(“spring.user”)
讀取以spring.user爲前綴的屬性文件,配置實體類
@EnableConfigurationProperties(UserPorperties.class)
將使用了@ConfigurationProperties 註解的類注入到spring容器中。
如果一個配置類只配置@ConfigurationProperties註解,而沒有使用@Component,那麼在IOC容器中是獲取不到properties 配置文件轉化的bean。
簡單來說@EnableConfigurationProperties 相當於把使用 @ConfigurationProperties 的類進行了一次注入。
@ConditionalOnProperty 屬性條件判斷,判斷指定的屬性是否有指定的值,滿足條件纔會初始化bean。
類似UserAutoConfigure這樣的自動化配置類是所有自動化配置組件的核心入口。
我們需要的就是在引入了user-spring-boot-starter的依賴後,在spring容器啓動的時候,加載到這個自動化配置類,那麼就可以初始化UserClient,完成自動化配置。
加載自動化配置類的三種方式
由於Spring boot默認掃描的是跟啓動類平級的包。如果我們的Start跟啓動類不在同一個主包下,那麼就需要通過其他手段使得容器啓動加載到UserAutoConfigure自動化配置類。
方式一:創建spring.factories屬性配置文件
在user-spring-boot-starter工程下的/resources/META-INF目錄下創建spring.factories屬性配置文件,並在裏面指定自動化屬性配置類的全路徑。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.laowan.user.autoconfig.UserAutoConfigure
這是由於在spring boot開啓了自動化配置註解後,在容器啓動時,會自動加載所有
/resources/META-INF下的spring.factories文件,讀取org.springframework.boot.autoconfigure.EnableAutoConfiguration配置屬性,組成一個集合,然後去遍歷加載所有的自動化配置類。
建立demo工程測試:
1、引入user-spring-boot-starter的依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.laowan</groupId>
<artifactId>user-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2、配置屬性文件
spring.user.enabled=true
spring.user.name=laowan
3、編寫單元測試類
@SpringBootTest
@Slf4j
class DemoApplicationTests {
@Autowired
UserClient userClient;
@Test
void userClientTest() {
log.info(userClient.getName());
}
}
4、執行結果
說明我們的自動化配置生效
方式二:通過定義@EnableXXX註解來加載自動化配置文件
在user-spring-boot-starter工程下,新建@EnableUserClient註解,其中最核心的是通過
@Import註解注入了UserAutoConfigure.class。這樣在引用工程的啓動類上只要添加了@EnableUserClient註解,那麼就會加載到UserAutoConfigure自動化配置類
```javascript
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({UserAutoConfigure.class})
public @interface EnableUserClient {
}
測試:
@SpringBootApplication
@EnableUserClient
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
先註釋掉spring.factories中的配置項,然後執行單元測試,正確打印出配置屬性spring.user.name的值,說明我們的自動化配置生效。
方式三:通過@SpringBootApplication註解指定掃描的基礎包路徑
在測試工程demo中,配置@SpringBootApplication的屬性scanBasePackages
@SpringBootApplication(scanBasePackages = {"com.laowan.demo","com.laowan.user.autoconfig"})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
通過@SpringBootApplication註解的scanBasePackages 屬性,指定掃描的包路徑。
這裏注意,一定要首先指定自己工程的根路徑,然後再執行自動化配置類的包路徑。
不然就只會掃描自動化配置類的包路徑,自己工程就不會掃描導致啓動出錯。
測試:
執行單元測試,發現配置屬性spring.user.name的值仍然可以正常打印,說明我們的自動化配置成功。
三種方式的比較:
三種加載自動化配置的方式,其核心都是解決Spring boot工程啓動只會默認掃描跟啓動類平級的包,導致其他不同包下的自動化配置類XXXAutoConfigure.class加載不到的問題。
方式一是通過spring.factories文件中配置org.springframework.boot.autoconfigure.EnableAutoConfiguration屬性指定自動化配置類XXXAutoConfigure.class的全路徑,是最主流的方式。
方式二通過自定義@EnableXXX註解並結合@Import註解加載自動化配置文件,在很多組件中也很常見,比如:
@EnableResourceServer
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ResourceServerConfiguration.class})
public @interface EnableResourceServer {
}
@EnableDiscoveryClient
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableDiscoveryClientImportSelector.class})
public @interface EnableDiscoveryClient {
boolean autoRegister() default true;
}
方式三主要是對自動化配置原理的一種驗證,實際項目中不推薦。
自動化配置原理核心:
就是想辦法在spring容器啓動的時候,掃描到自動化配置類,然後根據屬性類和條件註解去聲明Bean,完成自動化配置。
實現配置項提示功能
在user-spring-boot-starter工程的/resources/META-INF目錄下創建spring-configuration-metadata.json
{
"properties": [
{ "name": "spring.user.name",
"defaultValue": "laowan",
"description": "用戶名稱.",
"type": "java.lang.String"
},
{ "name": "spring.user.enabled",
"defaultValue": false,
"description": "是否啓用user組件.",
"type": "java.lang.Boolean"
}
]
}
這樣在引用工程中配置相關屬性,就會出現提示了。
通用規定
1、spring官方自己定義的starter組件,命名規則:spring-boot-starter-組件名
如:
spring-boot-starter-web
spring-boot-starter-jdbc
spring-boot-starter-security
非官方定義的start組件,命名規則:組件名-spring-boot-starter
如:
mybatis-spring-boot-starter
2、自動化配置類命名:XXXAutoConfiguration
3、starter組件的常規目錄結構分析
一般分爲2個工程,一個xxx-spring-boot-starter工程,通過spring.provides指定依賴服務模塊;
一個xxx-spring-boot-starter-autoconfigure模塊,通過定義spring.factories指定自動化配置文件加載路徑,定義spring-configuration-metadata.json實現自動化配置。
核心註解
在構建starter的過程中,涉及到一些註解
@EnableAutoConfiguration 開啓自動化配置功能,包含在@SpringBootApplication註解中,可以通過exclude屬性,過濾掉一些不需要開啓自動化配置的組件。
@Import 通過快速導入的方式實現把實例加入spring的IOC容器中,@Import只能用在類上
@ConfigurationProperties(“spring.user”) 加載前綴爲spring.user的屬性去配置當前類,但是並不會加載到spring容器中,需要配合@Component或者@EnableConfigurationProperties去使用。
@EnableConfigurationProperties(UserPorperties.class) 使 使用了@ConfigurationProperties註解配置的類生效,也就是注入到spring容器中
條件註解@Conditional
可以放在加了@Configuration的配置類上面,也可以放在使用@Bean定義bean的時候。用來判斷是否開啓配置或是否注入bean。
@ConditionalOnBean:當容器中有指定的Bean的條件下
@ConditionalOnClass:當類路徑下有指定的類的條件下
@ConditionalOnExpression:基於SpEL表達式作爲判斷條件
@ConditionalOnJava:基於JVM版本作爲判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查找指定的位置
@ConditionalOnMissingBean:當容器中沒有指定Bean的情況下
@ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下
@ConditionalOnNotWebApplication:當前項目不是Web項目的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑下是否有指定的資源
@ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者在有多個Bean的情況下,用來指定首選的Bean
@ConditionalOnWebApplication:當前項目是Web項目的條件下
總結
1、自動化配置的原理:
(1)、加載自動化配置類
通過@EnableAutoConfiguration開啓自動化配置機制,原理是通過類加載器去掃描目錄下所有spring.factories文件,讀取org.springframework.boot.autoconfigure.EnableAutoConfiguration屬性,然後去加載XXXAutoConfigure自動化配置類。這個是通用的加載方式,適合批量默認自動開啓的組件。
針對某些特定組件,沒有定義spring.factories文件,則需要通過在啓動類上添加@EnableXXX的註解,通過@Import導入指定的自動化配置類,這種方式適合單一控制,默認不開啓自動化配置的組件。
(2)、根據讀取到的自動化配置類,完成相關配置過程
XXXAutoConfigure自動化配置類中根據spring boot相關注解,讀取相關屬性文件,並根據@Conditional條件註解判斷是否開啓自動化配置,是否實例化Bean。
2、自動化配置類加載的三種方式
3、怎麼自定義一個starter組件以及相關的規範
4、自動化配置過程中,常見註解的說明
spring boot自動化配置的原理,你明白了嗎?
覺得有用,記得點贊加關注。