得物面試:Springboot自動裝配機制是什麼?如何控制一個bean 是否加載,使用什麼註解?

文章很長,且持續更新,建議收藏起來,慢慢讀!瘋狂創客圈總目錄 博客園版 爲您奉上珍貴的學習資源 :

免費贈送 :《尼恩Java面試寶典》 持續更新+ 史上最全 + 面試必備 2000頁+ 面試必備 + 大廠必備 +漲薪必備
免費贈送 :《尼恩技術聖經+高併發系列PDF》 ,幫你 實現技術自由,完成職業升級, 薪酬猛漲!加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷1)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷2)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷3)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領

免費贈送 資源寶庫: Java 必備 百度網盤資源大合集 價值>10000元 加尼恩領取


得物面試:Springboot自動裝配機制是什麼?如何控制一個bean 是否加載,使用什麼註解?

尼恩特別說明: 尼恩的文章,都會在 《技術自由圈》 公號 發佈, 並且維護最新版本。 如果發現圖片 不可見, 請去 《技術自由圈》 公號 查找

尼恩說在前面

在40歲老架構師 尼恩的讀者交流羣(50+)中,最近有小夥伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、希音、百度、網易、美團、螞蟻、得物的面試資格,遇到很多很重要的相關面試題:

Springboot自動裝配機制是什麼?

如何控制一個bean 是否加載,使用什麼註解?

starter的作用是什麼? 你用過哪些starter?

最近有小夥伴在面試得物,又被問到了相關的面試題,可以說,這個問題是逢面必問。

小夥伴沒有系統的去梳理和總結,所以支支吾吾的說了幾句,面試官不滿意,面試掛了。

所以,尼恩給大家做一下系統化、體系化的梳理,使得大家內力猛增,可以充分展示一下大家雄厚的 “技術肌肉”,讓面試官愛到 “不能自已、口水直流”

最終,機會爆表,實現”offer自由” 。

當然,這道面試題,以及參考答案,也會收入咱們的 《尼恩Java面試寶典PDF》V175版本,供後面的小夥伴參考,提升大家的 3高 架構、設計、開發水平。

《尼恩 架構筆記》《尼恩高併發三部曲》《尼恩Java面試寶典》的PDF,請到文末公號【技術自由圈】獲取

本文作者:

  • 第一作者 Jayen (Jayen 是尼恩團隊高級架構師,負責寫此文的第一稿,初稿 )
  • 第二作者 尼恩 (42歲老架構師, 負責提升此文的 技術高度,讓大家有一種 俯視 技術的感覺

SpringBoot 自動裝配和模塊化、複用化的關係

SpringBoot 自動裝配使得開發人員可以輕鬆地搭建、配置和運行應用程序,而無需手動管理大部分的 bean 和配置。

Spring Boot 的自動裝配機制與模塊化和複用化密切相關,它們之間存在着相互促進和互補的關係。

模塊化

  • Spring Boot 通過 Spring Starter 的方式提供了一種模塊化的解決方案。每個 Starter 都是一個獨立的模塊,它包含了特定功能的配置、依賴和自動裝配。
  • 開發者可以根據項目的需求選擇性地引入需要的 Starter,從而實現功能上的模塊化。這使得項目結構更清晰、功能更獨立,並且可以根據需求進行定製和組合。
  • 同時,Spring Boot 本身也是一個模塊化的框架,它將各種功能劃分爲不同的模塊,如 Web、數據訪問、安全等,使得開發者能夠根據需要選擇性地引入和使用這些功能模塊。

複用化

  • Spring Boot 的自動裝配機制大大提高了代碼的複用性。通過自動掃描和自動裝配,Spring Boot 能夠將各種功能模塊自動集成到應用程序中,避免了重複編寫和配置的工作。
  • Starter 的設計也是爲了提高代碼的複用性。開發者可以將常用的功能打包成 Starter,供其他項目引入和使用,從而避免了重複開發相似的功能。
  • 此外,Spring Boot 的約定大於配置的原則也促進了代碼的複用。通過統一的項目結構、配置規範和命名規範,開發者能夠更容易地理解和使用他人編寫的代碼,提高了代碼的可維護性和可複用性。

總的來說,Spring Boot 的自動裝配機制使得模塊化和複用化更加便捷和高效。開發者可以根據項目的需求選擇性地引入功能模塊,同時也可以將常用的功能封裝成 Starter,以供其他項目複用。

這種模塊化和複用化的設計理念使得 Spring Boot 在開發中得到了廣泛的應用和認可。

正因爲如此,SpringBoot 自動裝配是每個 Java Coder 逃不開的話題,SpringBoot 自動裝配 也是面試中的重點內容。

想回答好這個問題,首先我們要抓住幾個重點:

  1. 什麼是 SpringBoot 自動裝配?
  2. SpringBoot 是如何實現自動裝配的?如何實現按需加載?
  3. 如何自定義一個自己的可以服用的 SpringBoot starter 起步依賴?

什麼是 SpringBoot 自動裝配?

SpringBoot 會根據類路徑中的 jar包、類,爲 jar 包裏的類進行自動配置,這樣就可以大大的減少配置的數量。

簡單點說,就是 SpringBoot 會根據定義在 classpath 下的類,自動給你生成一些 Bean,並且加載到 Spring 的 Context 中。

那麼,它的原理是什麼呢?哪些 Bean 可以自動裝配到容器裏面呢?

其實在 SpringBoot 內部,會讀取 classpath 下 META-INF/spring.factories 文件中的所配置的類的全類名。

我們可以找到 key爲 org.springframework.boot.autoconfigure.EnableAutoConfiguration 對應的值。

可以發現就是一系列的 xxxAutoConfiguration,隨便找兩個類看一下,就是標記了 @Configuration 的配置類,並且通過一些條件註解,來判斷、決定哪些 Bean 應該自動注入到容器中。

⚠️注意:

在SpringBoot 2.7 版本後,spring.factories 方式已經被標記爲廢棄,

SpringBoot 2.7 版本後建議使用新的配置文件:

/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

但是,在加載到時候,會同時加載這兩個文件。

SpringBoot 是如何實現自動裝配的?如何實現按需加載?

SpringBoot 是如何實現自動裝配的,先看一個大概步驟:

  1. 組件掃描(Component Scanning):Spring Boot 使用組件掃描來查找和註冊標有特定註解的 bean。默認情況下,Spring Boot 將掃描主應用程序類所在的包及其子包下的所有類。標有 @Component@Service@Repository@Controller 註解的類將被註冊爲 bean。

  2. 條件化的 bean 註冊(Conditional Bean Registration):Spring Boot 使用條件化的 bean 註冊來根據條件自動註冊特定的 bean。這些條件可以基於環境屬性、系統屬性、類路徑是否包含特定類等。當條件滿足時,相應的 bean 將被註冊到 Spring 的應用程序上下文中。

  3. 自動配置(Auto-Configuration):Spring Boot 基於類路徑中的 jar 包和已配置的條件自動配置應用程序上下文。Spring Boot 提供了大量的自動配置類,這些類負責根據條件註冊特定的 bean,以及設置應用程序的默認配置。這樣,開發人員就無需手動配置大部分常見的 bean 和配置,而是可以依賴於 Spring Boot 的自動配置。

在這裏插入圖片描述

這個圖其實就是我們前面說的流程,SpringBoot 會去掃描指定的配置類,然後通過條件判斷是否加載 Bean。

Spring Boot 實現自動裝配的幾個核心註解

Spring Boot 實現自動裝配的幾個核心註解有哪些呢?還是先看一張圖:

在這裏插入圖片描述

Spring Boot 實現自動裝配的幾個核心註解包括:

  1. @SpringBootApplication:這是一個組合註解,相當於同時使用了 @Configuration@EnableAutoConfiguration@ComponentScan 註解。它標記了主應用程序類,並告訴 Spring Boot 開始組件掃描、自動配置和裝配。
  2. @EnableAutoConfiguration:該註解用於啓用 Spring Boot 的自動配置功能。它會根據應用程序的依賴關係和當前環境,自動註冊所需的 bean。
  3. @ComponentScan:該註解用於啓用組件掃描,以便 Spring Boot 可以自動發現和註冊標有 @Component@Service@Repository@Controller 註解的類。
  4. @ConditionalOnClass@ConditionalOnMissingClass:這兩個條件化註解用於根據類路徑上是否存在特定的類來決定是否註冊 bean。@ConditionalOnClass 在類路徑上存在指定類時生效,而 @ConditionalOnMissingClass 在類路徑上不存在指定類時生效。
  5. @ConditionalOnBean@ConditionalOnMissingBean:這兩個條件化註解用於根據是否存在特定的 bean 來決定是否註冊 bean。@ConditionalOnBean 在容器中存在指定的 bean 時生效,而 @ConditionalOnMissingBean 在容器中不存在指定的 bean 時生效。
  6. @ConditionalOnProperty:該條件化註解用於根據配置屬性的值來決定是否註冊 bean。它可以根據配置文件中的屬性值來決定是否啓用或禁用特定的 bean。

這些註解結合在一起,爲 Spring Boot 提供了自動裝配的能力。

從源碼層面來說,Spring Boot 自動裝配的核心實現,主要就是依賴 @SpringbootApplication 這個註解,它又是個組合註解,由3部分組成:

  1. @SpringBootConfiguration,標註是一個配置類
  2. @EnableAutoConfiguration,開啓自動裝配
    1. @AutoConfigurationPackage,指定默認的包規則,將主程序類所在包及其子包下的組件掃描到容器裏
    2. @Import(AutoConfigurationImportSelector.class),導入AutoConfigurationImportSelector,並通過 selectImports 方法讀取 META-INF/spring.factories 文件中配置的全類名,並按照條件過濾,注入需要的 bean
  3. @ComponentScan,配置掃描路徑,用來加載 bean

接下來我們就順着源碼,來和大家詳細聊聊 SpringBoot 自動裝配是如何實現的。

源碼解析: @SpringBootApplication 註解

首先我們找到主程序類,也就是程序的入口,執行 main 方法的地方。

@SpringBootApplication
public class BaseApplication {
	
  public static void main(String[] args) {
		SpringApplication.run(BaseApplication.class, args);
	}
}

@SpringBootApplication 標註在主程序類上,說明這個類是主配置類,SpringBoot 項目會運行這個類的 main 方法啓動應用。

我們繼續往裏跟一下,看看@SpringBootApplication 這個註解的廬山真面目。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  ... ...
}

典型的組合註解,最上面4個是 Java 提供的元註解,

重點看下 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 這三個註解。

源碼解析: @SpringBootConfiguration註解

先來看下 @SpringBootConfiguration:

@Configuration
@Indexed
public @interface SpringBootConfiguration {
	... ...
}

可以看到它也是個組合註解,除去元註解,可以看到有一個老朋友 @Configuration,

@Configuration 用於聲明某個類是 SpringBoot 的配置類。配置類也是容器中的一個組件,底層也是通過 @Component.

@Component
public @interface Configuration {
  ... ...
}

源碼解析: @EnableAutoConfiguration註解

接着我們來看下 @EnableAutoConfiguration :

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  ... ...
}

@EnableAutoConfiguration 也是一個組合註解,

@EnableAutoConfiguration 的作用其實就是告訴 SpringBoot 開啓自動裝配的功能,這樣自動配置才能生效。

源碼解析: @AutoConfigurationPackage註解

我們來進一步分析,看下 @AutoConfigurationPackage。

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
	... ...
}

它的底層其實是通過 @Import 給容器導入一個組件,@Import 註解的作用是在 Spring 配置類中用於導入其他配置類,從而將其配置信息合併到當前配置類中,以便統一管理和組織配置信息。

如果不瞭解 @Import , 那麼以下是 @Import 註解的一個簡單示例:

假設有一個配置類 TransactionConfig 用於配置事務管理器。

@Configuration
public class TransactionConfig {
    // 事務管理器配置
    @Bean
    public PlatformTransactionManager transactionManager() {
        // 配置事務管理器
    }
}

另設計一個配置類 DataSourceConfig 用於配置數據源,那麼,配置類 DataSourceConfig 可以使用 @Import 註解將 TransactionConfig 導入到自己內部 ,從而在配置數據源的時候,具備統一管理 事務 配置。

@Configuration
@Import(TransactionConfig.class)
public class DataSourceConfig {
    // 數據源配置
    @Bean
    public DataSource dataSource() {
        // 配置數據源
    }
}

在上面的示例中,DataSourceConfig 配置類使用 @Import(TransactionConfig.class) 註解導入了 TransactionConfig 配置類。這樣一來,DataSourceConfig 就可以統一管理數據源和事務管理器的配置,使得配置信息更加清晰和有序。

需要注意的是,@Import 註解只是將其他配置類導入到當前配置類中,@Import 註解並不會觸發其他配置類中的 bean 的註冊過程。

回到 SpringBoot的自動裝配源碼。

@AutoConfigurationPackage 導入的組件是 AutoConfigurationPackages.Registrar,將主配置類所在的包及其子包裏面所有的組件掃描到 Spring 容器裏。

AutoConfigurationPackages.Registrar,這個組件的作用是爲自動配置包註冊一個 BeanDefinition。

在這裏插入圖片描述

可以debug查看具體的值:

在這裏插入圖片描述

就是通過以上這個方法,獲取掃描的包路徑,然後通過AutoConfigurationPackages.register()方法 去 註冊 自動配置的Bean。

來看一下AutoConfigurationPackages.register() 方法,這個是 Spring Boot 中用於註冊自動配置包的靜態方法。

這裏 register() 的作用是將指定的包路徑註冊到 Spring 容器中,以便 Spring Boot 能夠在這些包下進行自動配置。

register() 方法接受的第一個參數 BeanDefinitionRegistry 對象,用於註冊 BeanDefinition。

在這裏插入圖片描述

代碼中,DefaultListableBeanFactory#registerBeanDefinition 方法的作用是向容器註冊一個新的 BeanDefinition。

順便說說 registerBeanDefinition 方法,這個方法會將一個 BeanDefinition 對象添加到容器的 beanDefinitionMap 中,從而使得容器在實例化和管理 bean 時能夠根據註冊的信息進行相應的處理。以下是 registerBeanDefinition 方法的主要作用:

  1. 註冊新的 BeanDefinition:該方法用於註冊一個新的 BeanDefinition 對象到容器中。這個 BeanDefinition 包含了 bean 的元數據信息,如 bean 的類名、作用域、依賴關係等。
  2. 檢查是否已存在同名的 BeanDefinition:在向容器註冊新的 BeanDefinition 之前,會先檢查容器中是否已存在同名的 BeanDefinition。如果已存在同名的 BeanDefinition,並且允許覆蓋,則會替換已存在的 BeanDefinition;否則會拋出異常。
  3. 更新容器的 beanDefinitionMap:註冊新的 BeanDefinition 後,會將其添加到容器的 beanDefinitionMap 中,其中 key 爲 bean 的名稱,value 爲對應的 BeanDefinition 對象。
  4. 更新容器的 beanNameSet 集合:同時,也會更新容器的 beanNameSet 集合,該集合包含了容器中所有註冊的 bean 的名稱。

總的來說,registerBeanDefinition 方法的作用是向 Spring 容器註冊一個新的 BeanDefinition,使得容器能夠根據這個註冊信息正確地實例化和管理 bean.

回到前面 AutoConfigurationPackages.register()方法。

這個方法中,通過 beanDefinition.setBeanClass(BasePackages.class) 設置了BeanClass 類型,具體源碼如下:

        //**創建bean定義
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		//**class爲BasePackages
		beanDefinition.setBeanClass(BasePackages.class);
		//**設置packageNames爲構造函數參數值
		beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		//**註冊名稱爲AutoConfigurationPackages的bean
		registry.registerBeanDefinition(BEAN, beanDefinition);

這裏BasePackages.class表示,Spring項目啓動時就會加載該包和其子包的帶有特定註解的類到Spring容器中。這些註解有@Component、@Bean、@Controller、@Service、@Repository等等。

呵呵, 這裏扯得太遠了。

再一次, 回到 AutoConfigurationPackages.register() ,具體作用如下:

  1. 註冊自動配置包AutoConfigurationPackages.Registrar 在 Spring Boot 應用程序啓動時會被觸發,它會掃描當前應用程序的主類所在的包及其子包,並將這些包路徑註冊爲自動配置包。這樣一來,Spring Boot 就知道在哪些包下尋找自動配置類。

  2. 啓用自動配置類的掃描:註冊自動配置包後,Spring Boot 將會在這些包下掃描並加載自動配置類。自動配置類是通過條件化註冊機制實現的,它們會根據條件來判斷是否需要註冊到 Spring 容器中。

總的來說,AutoConfigurationPackages.Registrar 的作用是爲自動配置包註冊 BeanDefinition,從而告訴 Spring Boot 在哪些包下掃描並加載自動配置類。這是 Spring Boot 實現自動配置的重要步驟之一。

源碼解析:AutoConfigurationImportSelector 類

再一次看 @EnableAutoConfiguration 註解


@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  ... ...
}

@EnableAutoConfiguration 也是一個組合註解, 通過 @Import 給容器導入 AutoConfigurationImportSelector 組件。

AutoConfigurationImportSelector 是 Spring Boot 中的一個核心組件。

簡單來說,AutoConfigurationImportSelector 用於實現自動配置的選擇和導入。

AutoConfigurationImportSelector` 的主要作用是根據類路徑上的條件和配置,選擇性地導入自動配置類,以便根據應用程序的需求進行自動配置。

具體來說,AutoConfigurationImportSelector 實現了 ImportSelector 接口,其中的 selectImports() 方法用於選擇需要導入的自動配置類。

Spring Boot 在啓動時會自動調用 AutoConfigurationImportSelector,根據一系列條件來決定需要導入哪些自動配置類。

AutoConfigurationImportSelector 主要的工作包括:

  1. 根據條件選擇自動配置類:根據一系列條件,如類路徑上的 jar 包、配置文件、註解等,決定需要導入哪些自動配置類。這些條件包括 @ConditionalOnClass@ConditionalOnMissingClass@ConditionalOnBean@ConditionalOnMissingBean 等註解所定義的條件。
  2. 執行條件判斷:根據條件判斷是否需要導入特定的自動配置類。如果滿足條件,則將自動配置類的全限定名添加到結果列表中。
  3. 返回結果:最終返回一個包含了需要導入的自動配置類的數組,供 Spring Boot 自動配置機制使用。

通過 AutoConfigurationImportSelector,Spring Boot 實現了自動配置的機制,能夠根據應用程序的需求,動態地選擇性地導入自動配置類,以簡化應用程序的配置和部署過程。

AutoConfigurationImportSelector 將所有需要導入到組件以全類名的方式返回,這些組件會被添加到容器,會給容器導入很多自動配置類(xxxAutoConfiguration),這就避免了我們手動編寫配置注入功能的工作。

重點看下 AutoConfigurationImportSelector 這個類:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  ... ...
}

public interface DeferredImportSelector extends ImportSelector {
  ... ...
}

public interface ImportSelector {
  // 獲取所有符合條件的類的全限定類名
  String[] selectImports(AnnotationMetadata importingClassMetadata);
}

AutoConfigurationImportSelector 類實現了 ImportSelector 接口,也就實現了這個接口中的 selectImports 方法,該方法主要用於獲取所有符合條件的類的全限定類名,這些類需要被加載到 IoC 容器中。

簡單看下 AutoConfigurationImportSelector 內部selectImports 的實現:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
  	// 判斷自動裝配開關是否打開
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
  	// 獲取所有需要裝配的bean
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

在 Spring 中,ImportSelector 接口是用於編程式地選擇並導入其他配置類的一種方式。AutoConfigurationImportSelector 就是一個典型的實現了 ImportSelector 接口的類,它根據一系列條件選擇性地導入自動配置類,實現了自動配置的機制。

selectImports 方法是 AutoConfigurationImportSelector 接口的一個 實現方法,用於選擇需要導入的配置類或者普通的 Java 類。 具體來說,selectImports 方法有以下作用:

  1. 選擇需要導入的配置類:在 selectImports 方法中,可以編寫邏輯來根據一定的條件選擇需要導入的配置類。這些條件可以是任何自定義的邏輯,如類路徑上的條件、環境屬性、配置文件等。
  2. 返回導入的配置類selectImports 方法返回一個字符串數組,數組中包含了需要導入的配置類的全限定名。這些配置類將會被 Spring 容器自動導入,並納入容器的管理。
  3. 靈活地實現自定義的導入邏輯:通過實現 selectImports 方法,可以實現靈活的自定義邏輯,根據應用程序的需求選擇性地導入配置類。這樣可以使得配置更加靈活、可擴展。

上面的代碼,用到selectImports, 這裏我們重點看下 getAutoConfigurationEntry 這個方法,

getAutoConfigurationEntry 主要負責加載自動配置類,核心調用鏈路如下:

在這裏插入圖片描述

我們來分析一下具體源碼內容:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  	// 判斷自動裝配開關是否打開,默認打開,spring.boot.enableautoconfiguration=true,可以在配置文件配置
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
  	// 獲取 @EnableAutoConfiguration 註解的 exclude 和 excludeName
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
  	// 讀取 spring.factories 文件的全類名,這裏會讀取所有 starter 下的該文件
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
  	// 這裏就是 @Conditional 條件註解生效的地方,過濾需要注入的 bean
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

getAutoConfigurationEntry 用於獲取自動配置條目(AutoConfigurationEntry)的信息。

具體來說,getAutoConfigurationEntry 方法的作用是根據條件獲取自動配置條目的信息,其中,自動配置條目包含了需要導入的自動配置類以及自動配置條件信息,最終生成需要導入的自動配置類的條目信息。

自動配置條件信息,如 @ConditionalOnClass@ConditionalOnBean 等註解所定義的條件,可以根據這些條件,判斷需要導入哪些自動配置類。getAutoConfigurationEntry 方法就是用來獲取這些自動配置條目的信息,以便後續的處理和導入。

在這裏插入圖片描述

getAutoConfigurationEntry 返回的自動配置條目,通常包括了自動配置類的全限定名,以及與條件判斷相關的信息。

面試第一問:什麼是 SpringBoot 自動裝配?

在面試中,回答這個問題,一定要抓住關鍵點:

  • 自動裝配底層實現:組合註解,每個註解的作用。
  • 核心的類 AutoConfigurationImportSelector#selectImports,會返回要導入容器的全類名,數組形式的。
  • 獲取需要自動裝配的配置類,讀取 META-INF/spring.factories 文件。
  • 通過條件註解過濾讀取的配置類,條件滿足的類,纔去加載 bean。

把這幾個關鍵點說清楚,面試基本上沒有什麼問題。

面試第二問:如何控制一個Bean是否加載? 使用什麼註解?

如果說 Spring 沒有添加其他附加條件,這些配置中定義的 Bean ,都會被一股腦兒導入進 Spring 容器裏,這樣是非常消耗內存的。因此提供了很多條件註解,可以讓我們控制某一個配置是否生效。

除了避免一次性全部加載全量的bean導致內存被無效消耗之外,Spring Boot 中的條件註解(Conditional Annotations)的存在,還有幾個主要原因:

  1. 靈活配置:Spring Boot 需要在不同的環境下提供靈活的配置選項,以滿足各種需求。條件註解允許根據運行時環境、類路徑上的類是否存在、系統屬性等條件來決定是否啓用或禁用特定的配置。
  2. 依賴配置:條件註解,能根據應用程序的類路徑和依賴關係,自動配置需要的bean。條件註解允許僅在滿足依賴條件時配置,沒有滿足依賴條件場景,不進行bean配置。
  3. 模塊化:Spring Boot 可以配置大量的功能模塊 Feature Module,但不是每個應用都需要所有的功能模塊 Feature Module。通過條件註解,可以根據應用的需求選擇性地導入或排除特定的Feature Module,使應用更加輕量級和高效。

條件註解是 Spring Boot 中的一種重要機制,它提供了靈活的配置選項和自動配置功能,使得應用程序能夠根據不同的條件進行定製和優化。

SpringBoot條件註解

Spring 爲我們提供了條件化註解,可以讓我們控制 bean 在某種條件下才加載,主要就是 @Conditional 註解,通過指定條件,然後根據條件結果執行。

Spring 還爲我們提供了一些已有的條件可以讓我們直接使用:

  1. @ConditionalOnClass:當類路徑中存在指定的類時生效。
  2. @ConditionalOnMissingClass:當類路徑中不存在指定的類時生效。
  3. @ConditionalOnBean:當容器中存在指定的 Bean 時生效。
  4. @ConditionalOnMissingBean:當容器中不存在指定的 Bean 時生效。
  5. @ConditionalOnProperty:當指定的配置屬性存在且值符合條件時生效。
  6. @ConditionalOnResource:當類路徑下存在指定資源文件時生效。
  7. @ConditionalOnWebApplication:當應用是 Web 應用時生效。
  8. @ConditionalOnNotWebApplication:當應用不是 Web 應用時生效。

這些條件註解可以用於配置類、自動配置類以及普通的 Bean 定義上,通過設置不同的條件,可以靈活地控制 Spring Boot 中的配置和自動配置行爲。

@ConditionalOnClass

@ConditionalOnClass 用於指定當類路徑中存在某個特定的類時,纔會生效。

如果類路徑中不存在指定的類,則該配置不生效。

這個註解通常用於自動配置類或者普通的配置類上,用於根據特定的類是否存在來決定是否應用該配置。

下面是一個@ConditionalOnClass 的簡單示例:

@Configuration
@ConditionalOnClass(name = "com.crazymaker.circle.Foo")
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

在這個示例中,MyConfiguration 是一個配置類,使用了 @ConditionalOnClass 註解。

當類路徑中存在 com.crazymaker.circle.Foo 這個類時,MyConfiguration 配置類纔會生效,從而將 MyBean Bean 註冊到容器中。

如果類路徑中不存在 com.crazymaker.circle.Foo 這個類,則 MyConfiguration 配置類不會生效,MyBean 也不會被註冊到容器中。

@ConditionalOnMissingClass

@ConditionalOnMissingClass 用於指定當類路徑中不存在某個特定的類時,纔會生效。

如果類路徑中存在指定的類,則該配置不生效。

下面是一個@ConditionalOnMissingClass 的簡單示例:

javaCopy code@Configuration
@ConditionalOnMissingClass(name = "com.crazymaker.circle.Foo")
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

在這個示例中,當類路徑中不存在 com.crazymaker.circle.Foo 這個類時,MyConfiguration 配置類纔會生效,從而將 MyBean Bean 註冊到容器中。

如果類路徑中存在 com.crazymaker.circle.Foo 這個類,則 MyConfiguration 配置類不會生效,MyBean 也不會被註冊到容器中。

@ConditionalOnBean

@ConditionalOnBean 用於指定當容器中存在某個特定的 Bean 時,纔會生效。如果容器中不存在指定的 Bean,則該配置不生效。

這個註解通常用於自動配置類或者普通的配置類上,用於根據容器中是否存在指定的 Bean 來決定是否應用該配置。

下面是一個簡單的示例:

@Configuration
public class DependencyConfiguration {

    @Bean
    public MyDependency myDependency() {
        return new MyDependency();
    }

}



@Configuration
public class MyConfiguration {

    @Bean
    @ConditionalOnBean(MyDependency.class)
    public MyBean myBean(MyDependency myDependency) {
        return new MyBean(myDependency);
    }
}

在這個示例中,定義了兩個 Bean:myDependencymyBean

myBean 方法上使用了 @ConditionalOnBean(MyDependency.class) 註解,

表示當容器中存在 MyDependency 類型的 Bean 時,myBean 方法纔會生效,從而將 MyBean Bean 註冊到容器中。

如果容器中不存在 MyDependency 類型的 Bean,則 myBean 方法不會生效,MyBean 也不會被註冊到容器中。

@ConditionalOnProperty

@ConditionalOnProperty用於指定當指定的配置屬性存在且值符合條件時,纔會生效。如果指定的配置屬性不存在或者值不符合條件,則該配置不生效。

這個註解通常用於自動配置類或者普通的配置類上,用於根據配置屬性的存在和值來決定是否應用該配置。

下面是一個簡單的示例:

@Configuration
@ConditionalOnProperty(name = "myapp.feature.enabled", havingValue = "true", matchIfMissing = true)
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

在這個示例中,MyConfiguration 使用了 @ConditionalOnProperty 註解。

@ConditionalOnProperty 註解用到三個屬性:

  • 一個配置屬性 name爲 myapp.feature.enabled
  • 一個配置屬性 havingValue 爲 true
  • 一個配置屬性 matchIfMissing 爲 true

這表示當應用程序的配置文件中,如果存在 myapp.feature.enabled=true 的配置屬性時,MyConfiguration 配置類纔會生效,從而將 MyBean Bean 註冊到容器中。

如果應用配置文件中不存在 myapp.feature.enabled 這個屬性,或者值不是 true,則 MyConfiguration 配置類不會生效,MyBean 也不會被註冊到容器中。

此外,@ConditionalOnProperty還有一個matchIfMissing 屬性,表示如果配置文件中不存在該屬性,是否默認生效。

在這個示例中,matchIfMissing 設置爲 true 表示如果配置文件中不存在該屬性,也認爲條件匹配,配置生效。

以上是三個重要的條件註解,其他的都是類似的,大家顧名思義就OK了。

如何自定義一個 SpringBoot starter?

Spring Boot 的自動裝配機制使得模塊化和複用化更加便捷和高效。

開發者可以根據項目的需求選擇性地引入功能模塊,同時也可以將常用的功能封裝成 Starter,以供其他項目複用。

SpringBoot 這種模塊化和複用化的設計理念使得 Spring Boot 在開發中得到了廣泛的應用和認可。

SpringBoot Starter用於簡化開發過程並提供各種功能的集成,Spring Boot 社區提供了許多常用的 Starter,常用的如下:

  1. spring-boot-starter-web:用於構建 Web 應用程序的 Starter,包括 Spring MVC、Embedded Tomcat、Jackson、Validation 等。
  2. spring-boot-starter-data-jpa:用於集成 Spring Data JPA 和 Hibernate 的 Starter,用於訪問和操作關係型數據庫。
  3. spring-boot-starter-data-mongodb:用於集成 Spring Data MongoDB 的 Starter,用於訪問和操作 MongoDB 數據庫。
  4. spring-boot-starter-security:用於集成 Spring Security 的 Starter,提供身份驗證和授權功能。
  5. spring-boot-starter-test:用於測試 Spring Boot 應用程序的 Starter,包括 JUnit、Spring Test、Spring Boot Test 等。
  6. spring-boot-starter-actuator:用於監控和管理 Spring Boot 應用程序的 Starter,包括健康檢查、指標、日誌級別設置等。
  7. spring-boot-starter-log4j2:用於集成 Log4j2 日誌框架的 Starter,用於記錄應用程序日誌。
  8. spring-boot-starter-mail:用於集成郵件發送功能的 Starter,包括 JavaMail 和 Spring Framework 的郵件支持。
  9. spring-boot-starter-cache:用於集成緩存支持的 Starter,包括 Spring 緩存抽象和常見的緩存實現,如 Ehcache、Redis、Caffeine 等。
  10. spring-boot-starter-actuator:用於添加生產就緒功能,如指標、健康檢查、審計、HTTP追蹤等。

這些 Starter 提供了各種功能的快速集成和配置,能夠大大加速 Spring Boot 應用程序的開發過程,並且得益於 Spring Boot 的自動配置機制,使用這些 Starter 通常可以減少很多繁瑣的配置工作。

如果 SpringBoot 官方的starter不夠用,那麼,如何自定義一個 starter ?

要自定義一個 Spring Boot Starter,需要遵循一些約定和最佳實踐,以確保咱們的額Starter 能夠被其他人方便地使用,並且與 Spring Boot 生態系統的其他部分無縫集成。

下面是創建一個自定義 Spring Boot Starter 的一般步驟:

  1. 定義 Starter 模塊:創建一個 Maven 或 Gradle 項目,作爲咱們的 Starter 模塊。該模塊應該包含要提供的功能、配置和依賴項。
  2. 創建自動配置類:編寫一個自動配置類,該類負責配置和初始化你的 Starter 提供的功能。這個類通常需要用 @Configuration 和其他條件註解(如 @ConditionalOnClass@ConditionalOnBean 等)標記。
  3. 提供默認配置:通過 META-INF/spring.factories 文件中提供默認的配置類。
  4. 創建 Starter 類:創建一個類作爲 Starter 的入口點。這個類通常會提供一些便捷的方法,以便用戶能夠輕鬆地在其項目中使用你的 Starter。
  5. 提供文檔和示例:編寫文檔說明你的 Starter 的用法和配置方式,並提供示例代碼。
  6. 發佈到 Maven 倉庫:將你的 Starter 打包,併發布到 Maven 中央倉庫或其他公共或私有的 Maven 倉庫中,以便其他用戶可以方便地引用和使用。

1.新建 SpringBoot 項目,引入 SpringBoot 相關依賴

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

2.創建 @Configuration 配置類,在配置類裏面聲明要注入的 Bean,還可以結合 @Conditional 條件註解,按需加載

package com.example.mystarter;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(MyStarterService.class)
public class MyStarterAutoConfiguration {

    // 自動配置的內容,例如註冊 Bean、初始化等
}

MyStarterAutoConfiguration.java:自動配置類,用於配置和初始化 Starter 提供的功能。

這個類通常需要用 @Configuration 和其他條件註解(如 @ConditionalOnClass@ConditionalOnBean 等)標記。

如果有需求,可以 寫一個 MyStarterProperties.java 用於定義 Starter 配置屬性的 POJO 類。

package com.example.mystarter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mystarter")
public class MyStarterProperties {

    // Starter 的配置屬性
}

3.在項目的 resources 包下創建 META-INF/spring.factories 文件,並且配置類的全限定名

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.mystarter.MyStarterAutoConfiguration

4.再其他工程引用自定義的 starter 即可

<dependency>
    <groupId>com.warm</groupId>
    <artifactId>custom-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

下面是這一個簡單starter的示例結構:

codemy-spring-boot-starter/
  ├── src/
  │   ├── main/
  │   │   ├── java/
  │   │   │   └── com/
  │   │   │       └── example/
  │   │   │           └── mystarter/
  │   │   │               ├── MyStarterAutoConfiguration.java
  │   │   │               └── MyStarterProperties.java
  │   │   └── resources/
  │   │       └── META-INF/
  │   │           └── spring.factories
  │   └── test/
  │       └── java/
  │           └── com/
  │               └── example/
  │                   └── mystarter/
  │                       └── MyStarterAutoConfigurationTest.java
  ├── pom.xml
  └── README.md

在這個示例中:

  • MyStarterAutoConfiguration 類是自動配置類,
  • MyStarterProperties 類是用於定義屬性的 POJO 類。
  • spring.factories 文件包含默認配置類的聲明。‘
  • 最後,README.md 文件提供了對 Starter 的說明和用法。

創建一個自定義的 Spring Boot Starter 的步驟如上,另外,建議在開始定義之前,先查閱 Spring Boot 官方文檔,並參考一些現有的 Starter 項目以獲取靈感和最佳實踐。

Spring Boot 的自動配置和Spring run方法的關係

這裏多提一嘴,Spring Boot 的自動配置與 Spring run 方法關係密切。

Spring run 方法是 Spring Boot 應用程序的入口。

Spring run 負責啓動 Spring 應用程序並執行一些初始化操作。而自動配置則是 Spring Boot 提供的一種機制,用於根據類路徑和依賴關係自動配置應用程序的功能。

Spring run 方法通常位於一個包含 @SpringBootApplication 註解的類中,如下所示:

javaCopy codeimport org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

在這個示例中,MyApplication 類上標記了 @SpringBootApplication 註解,

該註解包含了 @EnableAutoConfiguration@ComponentScan@Configuration 註解。

其中,@EnableAutoConfiguration 是啓用 Spring Boot 的自動配置機制的關鍵,它會自動掃描類路徑上的各種配置類,並根據條件自動配置應用程序的功能。

因此,當調用 SpringApplication.run(MyApplication.class, args) 方法啓動 Spring Boot 應用程序時,Spring Boot 將會自動掃描和加載類路徑上的各種自動配置類,並根據條件來決定是否應用這些配置。

這使得開發者能夠在不編寫繁瑣的配置代碼的情況下,快速構建和部署功能強大的 Spring Boot 應用程序。

總之,Spring Boot 的自動配置機制通過 @EnableAutoConfiguration 註解和 run 方法的調用實現了緊密的集成,使得開發者能夠輕鬆地構建和啓動功能豐富的 Spring Boot 應用程序。

關於啓動類 mian 方法中的 Spring run方法 方法的作用,以及它做了哪些事情。

1、推斷應用的類型是普通的項目還是 Web 項目
2、查找並加載所有可用初始化器 , 設置到 initializers 屬性中
3、找出所有的應用程序監聽器,設置到 listeners 屬性中
4、推斷並設置 main 方法的定義類,找到運行的主類

通過一個張圖,來帶大家簡單梳理一下這個過程, 下一次尼恩架構團隊給大家 來一版驚天地、泣鬼神的Spring run方法 原理和源碼解析 :

在這裏插入圖片描述

說在最後:有問題找老架構取經

Springboot起步依賴、自動裝配相關的面試題,是非常重要的面試題。

如果能按照以上的內容,對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。

最終,讓面試官愛到 “不能自已、口水直流”。offer, 也就來了。

在面試之前,建議大家系統化的刷一波 5000頁《尼恩Java面試寶典》V175,在刷題過程中,如果有啥問題,大家可以來 找 40歲老架構師尼恩交流。

另外,如果沒有面試機會,可以找尼恩來幫扶、領路。

尼恩已經指導了大量的就業困難的小夥伴上岸,前段時間,幫助一個40歲+就業困難小夥伴拿到了一個年薪100W的offer,幫助小夥伴實現了 逆天改命

技術自由的實現路徑:

實現你的 架構自由:

喫透8圖1模板,人人可以做架構

10Wqps評論中臺,如何架構?B站是這麼做的!!!

阿里二面:千萬級、億級數據,如何性能優化? 教科書級 答案來了

峯值21WQps、億級DAU,小遊戲《羊了個羊》是怎麼架構的?

100億級訂單怎麼調度,來一個大廠的極品方案

2個大廠 100億級 超大流量 紅包 架構方案

… 更多架構文章,正在添加中

實現你的 響應式 自由:

響應式聖經:10W字,實現Spring響應式編程自由

這是老版本 《Flux、Mono、Reactor 實戰(史上最全)

實現你的 spring cloud 自由:

Spring cloud Alibaba 學習聖經》 PDF

分庫分表 Sharding-JDBC 底層原理、核心實戰(史上最全)

一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之間混亂關係(史上最全)

實現你的 linux 自由:

Linux命令大全:2W多字,一次實現Linux自由

實現你的 網絡 自由:

TCP協議詳解 (史上最全)

網絡三張表:ARP表, MAC表, 路由表,實現你的網絡自由!!

實現你的 分佈式鎖 自由:

Redis分佈式鎖(圖解 - 秒懂 - 史上最全)

Zookeeper 分佈式鎖 - 圖解 - 秒懂

實現你的 王者組件 自由:

隊列之王: Disruptor 原理、架構、源碼 一文穿透

緩存之王:Caffeine 源碼、架構、原理(史上最全,10W字 超級長文)

緩存之王:Caffeine 的使用(史上最全)

Java Agent 探針、字節碼增強 ByteBuddy(史上最全)

實現你的 面試題 自由:

4800頁《尼恩Java面試寶典 》 40個專題

免費獲取11個技術聖經PDF:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章