小白都能聽懂的spring boot自動化配置原理

spring boot最核心的特性就是他的自動化配置特性,極大的減小了構建一個spring web工程的工做量。那麼你知道spring boot自動化配置的原理嗎?java

先直接自定義一個user-spring-boot-starter組件,感覺下自動化配置的魅力。
git

構建user-spring-boot-starter

pom依賴github

   <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>
<dependencies>

添加配置屬性web

spring.user.enabled=false
spring.user.name=laowan

建立UserPorperties屬性類使用配置屬性spring

@Data
@ConfigurationProperties("spring.user")
public class UserPorperties {
    private String name;
}

利用屬性類UserPorperties構建UserClient對象json

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爲前綴的屬性文件,配置實體類mybatis

@EnableConfigurationProperties(UserPorperties.class)  
將使用了@ConfigurationProperties 註解的類注入到spring容器中。
若是一個配置類只配置@ConfigurationProperties註解,而沒有使用@Component,那麼在IOC容器中是獲取不到properties 配置文件轉化的bean。
簡單來講@EnableConfigurationProperties 至關於把使用 @ConfigurationProperties 的類進行了一次注入。ide

@ConditionalOnProperty  屬性條件判斷,判斷指定的屬性是否有指定的值,知足條件纔會初始化bean。spring-boot

相似UserAutoConfigure這樣的自動化配置類是全部自動化配置組件的核心入口
咱們須要的就是在引入了user-spring-boot-starter的依賴後,在spring容器啓動的時候,加載到這個自動化配置類,那麼就能夠初始化UserClient,完成自動化配置。

加載自動化配置類的三種方式

因爲Spring boot默認掃描的是跟啓動類平級的包。若是咱們的Starter項目跟啓動類不在同一個主包下,那麼就須要經過其餘手段使得容器啓動時能加載到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工程測試:
一、引入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>

二、配置屬性文件

spring.user.enabled=true
spring.user.name=laowan

三、編寫單元測試類

@SpringBootTest
@Slf4j
class DemoApplicationTests {

    @Autowired
    UserClient userClient;

    @Test
    void userClientTest() {
        log.info(userClient.getName());
    }
}

四、執行結果

執行結果

說明咱們的自動化配置生效

方式二:經過自定義@EnableXXX註解來加載自動化配置文件
在user-spring-boot-starter工程下,新建@EnableUserClient註解,其中最核心的是經過@Import註解注入了UserAutoConfigure.class。這樣在引用工程的啓動類上只要添加了@EnableUserClient註解,那麼就會加載到UserAutoConfigure自動化配置類

@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"
    }
  ]
}

這樣在引用工程中配置相關屬性,就會出現提示了。

在這裏插入圖片描述

通用規定

一、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

二、自動化配置類命名:XXXAutoConfiguration

三、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)、加載自動化配置類
經過@EnableAutoConfiguration開啓自動化配置機制,原理是經過類加載器去掃描目錄下全部spring.factories文件,讀取org.springframework.boot.autoconfigure.EnableAutoConfiguration屬性,而後去加載XXXAutoConfigure自動化配置類。這個是通用的加載方式,適合批量默認自動開啓的組件。

針對某些特定組件,沒有定義spring.factories文件,則須要經過在啓動類上添加@EnableXXX的註解,經過@Import導入指定的自動化配置類,這種方式適合單一控制,默認不開啓自動化配置的組件。

(2)、根據讀取到的自動化配置類,完成相關配置過程
XXXAutoConfigure自動化配置類中根據spring boot相關注解,讀取相關屬性文件,並根據@Conditional條件註解判斷是否開啓自動化配置,是否實例化Bean。

二、自動化配置類加載的三種方式

三、怎麼自定義一個starter組件以及相關的規範

四、自動化配置過程當中,常見註解的說明

spring boot自動化配置的原理,你明白了嗎?

實戰代碼Git地址:https://github.com/StarlightWANLI/auto-config.git

更多精彩,關注我吧。

跟着老萬學java


本文分享自微信公衆號 - 跟着老萬學java(douzhe_2019)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索