SpringBoot(原理篇)

大家好,這里是 盛碼筆記

本篇我們來聊一聊 Spring Boot 的“魔法”是如何實現的。你可能已經用過 Spring Boot 快速搭建項目,但有沒有想過:為什么只需要引入幾個 starterSpring Boot 就能自動配置好整個應用環境?

這背后的核心機制就是:起步依賴(Starter Dependencies) 和 自動裝配(Auto-Configuration)。本文將從源碼角度帶你深入理解這兩個機制的底層原理,建議你具備一定的 Spring 基礎后再閱讀。

(一)起步依賴

依賴傳遞機制(Maven/Gradle)

依賴傳遞的話,在講maven的時候有提到過,下面是地址(這里就不展開了)

Maven詳細教程(包含安裝)-CSDN博客

  • 核心作用:Starter 本身是一個空項目,僅包含一個 pom.xml(或 Gradle 構建文件),通過 Maven 的依賴傳遞聚合了某個場景所需的全部依賴。

  • 示例spring-boot-starter-webpom.xml 中聲明了:

    <dependencies><dependency>spring-boot-starter</dependency><dependency>spring-boot-starter-json</dependency><dependency>spring-webmvc</dependency><dependency>spring-boot-starter-tomcat</dependency>
    </dependencies>
    
  • 效果:用戶只需引入 spring-boot-starter-web,即可自動獲得 Web 開發所需的所有庫(Tomcat、Jackson、Spring MVC 等)。

(二)自動配置

  • 原理: Spring Boot 在啟動時掃描 classpath,根據存在的類、Bean 定義以及屬性設置,自動配置 Spring 應用所需的各種組件(如 DataSource, JPA, MVC, Security 等)。
  • 實現:
    • @EnableAutoConfiguration 注解(通常由 @SpringBootApplication 包含)啟用自動配置。
    • 大量的 XxxAutoConfiguration 類(在 spring-boot-autoconfigure jar 中),這些類使用 @Configuration 定義配置,并使用 @ConditionalOnXxx(條件注解)來決定配置是否生效(例如 @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty)。

Bean的導入機制

1. @ComponentScan

作用
自動掃描指定包及其子包下所有被 @Component@Service@Controller@Repository 等注解標記的類,并注冊為Bean。

特點

  • 基于包路徑掃描:通過 basePackagesbasePackageClasses 指定掃描范圍。
  • 批量注冊:一次性注冊多個Bean。
  • 依賴組件注解:目標類必須被Spring的組件注解標記(如 @Component)。
  • 過濾規則:支持通過 includeFilters/excludeFilters 自定義掃描規則。
@Configuration
@ComponentScan(basePackages = "com.example.service",includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Service")
)
public class AppConfig {}

適用場景
項目中自定義組件的批量注冊(如Service、Controller層)。


2. @Import

作用
顯式導入一個或多個配置類(@Configuration)、普通類(視為Bean)或 ImportSelector/ImportBeanDefinitionRegistrar 的實現類。

特點

  • 精確導入:直接指定要導入的類(而非包掃描)。
  • 無需組件注解:被導入的類不需要@Component 標記。
  • 支持多種類型
    • 配置類:導入其他 @Configuration 類。
    • 普通類:直接作為Bean注冊。
    • ImportSelector:動態選擇導入類。
    • ImportBeanDefinitionRegistrar:編程式注冊Bean。
@Configuration
@Import({ DataSourceConfig.class,   // 導入配置類MyUtility.class,          // 普通類(注冊為Bean)MyImportSelector.class    // 動態選擇導入
})
public class AppConfig {}

適用場景

  • 導入第三方庫的配置類(如 DataSourceConfig)。
  • 注冊未被組件注解標記的類(如工具類)。

3. ImportSelector 接口

作用
通過編程方式動態選擇要導入的配置類或Bean類,返回類的全限定名字符串數組。

特點

  • 動態決策:根據運行時條件(如配置參數、環境變量)決定導入哪些類。
  • 解耦設計:將導入邏輯從 @Import 注解中分離。
  • 無侵入性:目標類無需實現任何接口或注解。

實現步驟

  1. 實現 ImportSelector 接口。
  2. 重寫 selectImports() 方法,返回要導入的類的全限定名。
  3. 通過 @Import 引入該 ImportSelector
public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// 根據當前的運行環境動態選擇配置文件(dev和prod環境)if (System.getenv("env").equals("prod")) {return new String[]{"com.example.ProdDataSourceConfig"};}else {return new String[] { "com.example.DevDataSourceConfig" };}}
}// 通過@Import引入
@Configuration
@Import(MyImportSelector.class)
public class AppConfig {}

適用場景
需要根據條件動態注冊Bean(如環境區分、特性開關)。

4. @Bean 方法

@Configuration 類中使用 @Bean 注解的方法:

@Configuration
public class AppConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource();}
}

特點

  • 適用于實例化過程復雜的 Bean(如連接池)
  • 可顯式控制 Bean 的初始化邏輯
5. ImportBeanDefinitionRegistrar(了解)

編程式注冊 BeanDefinition:

public class CustomRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {registry.registerBeanDefinition("customBean", new RootBeanDefinition(CustomBean.class));}
}

特點

  • 最底層的 Bean 注冊方式
  • 可操作 BeanDefinition 元數據(作用域/Lazy/初始化方法等)

源碼剖析

springboot版本為3.4.7

  1. 通過@SpringBootApplication是入口,點擊進去。

    通過下面的源碼可以發現,知道了為什么@SpringBootApplication掃描、注冊當前包及子包下的Bean了,還可以當配置類使用。

    // 注解使用范圍(類、接口或枚舉類型)
    @Target({ElementType.TYPE})   
    // 保留策略:注解信息保留在運行時
    @Retention(RetentionPolicy.RUNTIME)
    // 注解會被包含在 JavaDoc 中
    @Documented
    // 繼承:一個類使用了該注解,其子類將自動繼承該注解
    @Inherited// 這個注解相當于@Configuration的增強版(配置類)
    @SpringBootConfiguration
    // 自動配置的核心注解(啟用 Spring Boot 的自動配置機制)
    @EnableAutoConfiguration//  啟用 Spring 的組件掃描功能,并通過自定義過濾器排除某些類不被掃描到容器中
    // 不指定包路徑,默認掃描、注冊當前包及子包下的Bean
    @ComponentScan(excludeFilters = {@Filter(				// 排除過濾器type = FilterType.CUSTOM,				// 表示使用開發者自定義的過濾器邏輯classes = {TypeExcludeFilter.class}		// 指定具體的過濾器類
    ), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}   // 排除那些已經被標記為“自動配置”的類
    )}
    )
    public @interface SpringBootApplication {// 這里省略了
    }
    

    再看一下@SpringBootConfiguration里面,下文可知,@SpringBootConfiguration的功能相當于增強版的@Configuration

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    // 配置類注解的功能
    @Configuration
    // 提升性能,支持編譯時索引,加快組件掃描
    @Indexed
    public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
    }
    
  2. 再點擊@EnableAutoConfiguration,進去繼續看

    Spring Boot 自動裝配的核心是 @EnableAutoConfigurationAutoConfigurationImportSelector(實現 ImportSelector

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    // 將主配置類(即標注了 @EnableAutoConfiguration 的類)所在的包作為自動掃描的根包。
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
    }
    

    然后是@AutoConfigurationPackage,它的作用是將主配置類所在的包作為自動掃描的根包。(至于后續邏輯就不展開了)

    在 Spring Boot 自動配置過程中,Spring 需要知道哪些包是“主應用包”,即:

    • 包含主配置類的包;
    • 需要被自動掃描和處理的包;

    這些信息會被存儲在一個特殊的 Bean 中,例如通過 AutoConfigurationPackages.register(...) 注冊到容器中。

    其他自動配置類(如 DataSourceAutoConfiguration)會依賴這些包路徑來判斷是否啟用某些自動配置邏輯。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    // 從使用 @AutoConfigurationPackage 注解的類中讀取 basePackages 和 basePackageClasses 屬性;
    // 將這些包路徑注冊到 Spring 容器中;
    // 供后續 Spring Boot 自動配置機制使用。
    @Import({AutoConfigurationPackages.Registrar.class})
    public @interface AutoConfigurationPackage {String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
    }
    
  3. 接下來需要點進入AutoConfigurationImportSelector類里面

    • 該類實現了DeferredImportSelector接口,而DeferredImportSelector接口繼承于ImportSelector接口,所以實現了selectImports方法。這里與上文第三種導入Bean的方法類似,需要一個字符串數組。
    • 這里不清楚的就是getAutoConfigurationEntry,跳轉過去
    // 代碼部分省略
    /*** 根據注解元數據選擇需要導入的自動配置類。** 該方法通常用于 Spring Boot 的自動配置機制中,根據當前類上的注解信息* 決定應該導入哪些自動配置類(即返回對應的全限定類名數組)。** @param annotationMetadata 注解元數據,描述了當前類上使用的注解信息* @return 返回一個字符串數組,包含所有需要導入的自動配置類的全限定名*/
    public String[] selectImports(AnnotationMetadata annotationMetadata) {// 如果自動配置未啟用,則直接返回空數組,不導入任何配置類if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {// 獲取自動配置條目(包含要導入的配置類集合)AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);// 將配置類集合轉換為字符串數組并返回return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
    }
    

    點擊getAutoConfigurationEntry方法跳轉過去

    • 根據該方法的名稱和返回值可以初步推測該方法時返回一個自動配置的對象
    • 該方法圍繞configurations這個字符串列表集合,需要知道它是什么就再點擊getCandidateConfigurations過去
    /*** 獲取自動配置條目(包含要導入的自動配置類和被排除的類)** 該方法主要完成以下工作:* 1. 檢查是否啟用自動配置* 2. 獲取注解屬性* 3. 加載所有候選的自動配置類* 4. 去重處理* 5. 處理排除類* 6. 過濾不符合條件的配置類* 7. 發布自動配置導入事件** @param annotationMetadata 注解元數據,描述當前類上的注解信息* @return 返回一個 AutoConfigurationEntry 對象,包含自動配置類和排除類*/
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {// 1. 判斷是否啟用了自動配置,如果沒有啟用,則返回一個空的 AutoConfigurationEntryif (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {// 獲取注解屬性(例如 @EnableAutoConfiguration 上的 exclude 或 excludeName 屬性)AnnotationAttributes attributes = this.getAttributes(annotationMetadata);// 獲取所有的候選自動配置類(從 META-INF/spring.factories 中讀取)List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//去除重復的配置類configurations = this.removeDuplicates(configurations);//獲取需要排除的自動配置類(通過注解屬性 exclude / excludeName)Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);//  從候選配置類中移除被排除的類configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);// 將處理后的配置類和排除類封裝成 AutoConfigurationEntry 返回return new AutoConfigurationEntry(configurations, exclusions);}
    }
    

    點擊getCandidateConfigurations過去

    在這個方法里面可以知道這是從某個路徑中加載配置類,并返回字符串集合。

    /*** 加載所有候選的自動配置類*/
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 從 META-INF/spring/...imports 文件中加載自動配置類ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());// 獲取配置類列表List<String> configurations = importCandidates.getCandidates();// 校驗是否找到配置類,避免為空Assert.state(!CollectionUtils.isEmpty(configurations), "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");return configurations;
    }
    
  4. 獲取加載配置類的全路徑(包名+類名)

    采用debug斷點調試

    • getCandidateConfigurations方法里面打一個斷點
    • 然后運行主類即可

    可以發現this.autoConfigurationAnnotation.getName()獲取的值是org.springframework.boot.autoconfigure.AutoConfiguration

    結合上面代碼中長字符串的拼接加載配置類的完整路徑是

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

    在這里插入圖片描述

  5. 查看配置類

    怎么知道這個路徑對不對呢?直接找出來看一下就知道了唄

    項目左邊打開外部庫,根據這個路徑找對應的文件

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

    在這里插入圖片描述
    打開文件可以發現全都是全類名(包名+類名),再結合上面講Bean導入機制時,實現ImportSelector 接口的配置類可以全類名字符串數組加載、注冊Bean。

    在這里插入圖片描述

  6. 隨便點擊進入一個配置類,以GsonAutoConfiguration為例

    這里有代碼創建了Gson的Bean實例,所以很多類不需要手動聲明便可以直接使用,然后有一些以Conditional開頭的注解作用是按條件加載對應的Bean,后面會詳細講解。

    在這里插入圖片描述

按條件裝配

  1. @ConditionalOnClass

    • 作用: 當且僅當項目的類路徑 (classpath) 上存在指定的類時,才會加載被注解標記的配置(配置類、@Bean 方法、@Component 等)。

    • 場景:

      • 自動配置的核心: Spring Boot Starter 的自動配置類大量使用此注解。例如,DataSourceAutoConfiguration 可能這樣

        @Configuration
        @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
        public class DataSourceAutoConfiguration {// ... 只有在類路徑上有 DataSource 和 EmbeddedDatabaseType 時才配置數據源
        }
        

        這確保了只有當你引入了數據庫驅動(如 HikariCP, tomcat-jdbc)等依賴(它們會提供 DataSource 實現類)時,Spring Boot 才會嘗試自動配置數據源。避免了因為缺少依賴而拋出 ClassNotFoundException

      • 可選功能: 當你開發一個庫或 Starter,并且某些功能需要特定的第三方庫支持時,可以使用 @ConditionalOnClass 來確保只有用戶引入了該庫,你的功能才會被激活。

    • 注意事項: 指定的類名必須是全限定名com.example.SomeClass)。Spring Boot 通常使用 name 屬性指定字符串形式的類名(避免在編譯時就需要該類存在),但也支持直接使用 Class<?>[] value()

  2. @ConditionalOnMissingClass

    • 作用: 當且僅當項目的類路徑 (classpath)不存在指定的類時,才會加載被注解標記的配置。

    • 場景:

      • 提供默認實現或回退方案: 當某個功能有多個實現(如日志框架:Logback, Log4j2),你可以為其中一個實現(如 Logback)配置默認 Bean,但條件設置為類路徑上不存在另一個實現(如 Log4j2)的關鍵類。

        @Configuration
        @ConditionalOnMissingClass("org.apache.logging.log4j.core.LoggerContext") // 假設 Log4j2 的核心類
        public class DefaultLoggingConfiguration {@Beanpublic Logger myLogger() {// 配置默認的 Logback Logger}
        }
        

        這樣,如果用戶引入了 Log4j2,這個默認的 Logback 配置就不會生效。

      • 環境隔離: 在特定環境(如測試)下排除某些不需要的配置。

    • 注意事項: 同樣需要指定全限定名。使用相對較少,因為 @ConditionalOnClass@ConditionalOnBean/@ConditionalOnMissingBean 組合通常能覆蓋大部分需求。

  3. @ConditionalOnBean

    • 作用: 當且僅當 Spring 應用上下文 (IoC 容器)已經存在(已經定義或即將定義)指定類型、指定名稱或滿足指定條件的 Bean 時,才會加載被注解標記的配置。

    • 場景:

      • 依賴其他 Bean 的自動配置: 某個自動配置需要另一個 Bean 已經存在才能工作。例如,一個配置 JdbcTemplate 的自動配置類可能依賴于 DataSource Bean:

        @Configuration
        public class JdbcTemplateAutoConfiguration {@Bean@ConditionalOnBean(DataSource.class) // 只有容器中存在 DataSource Bean 時才創建 JdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}
        }
        

        確保 JdbcTemplate 只在有可用的 DataSource 時才被創建。

      • 按需配置擴展功能: 如果用戶配置了某個特定的 Bean(如自定義的 MySpecialService),則自動激活與之相關的額外功能或配置。

    • 注意事項:

      • 匹配條件可以指定 Bean 的類型 (Class<?>)、名稱 (String name) 或使用 @ConditionalOnSingleCandidate 等注解進一步限定。
      • Bean 定義順序很重要! 被依賴的 Bean(DataSource)必須在依賴它的 Bean(JdbcTemplate之前被定義(或至少在同一批處理中能被檢測到)。Spring Boot 的自動配置機制通過精心設計的加載順序來解決這個問題。在自定義配置時需要注意潛在的循環依賴或順序問題。
  4. @ConditionalOnMissingBean

    • 作用: 當且僅當 Spring 應用上下文 (IoC 容器)不存在(沒有定義,也沒有即將定義)指定類型、指定名稱或滿足指定條件的 Bean 時,才會加載被注解標記的配置。

    • 場景:

      • 提供默認 Bean 實現 (關鍵!): 這是 Spring Boot 自動配置的核心機制,也是用戶覆蓋默認配置的關鍵入口。自動配置類會使用此注解定義默認 Bean:

        @Configuration
        public class DataSourceConfiguration {@Bean@ConditionalOnMissingBean // 關鍵注解!如果用戶沒有自定義 DataSource Bean...public DataSource dataSource() {// ...則 Spring Boot 自動創建一個默認的 DataSource (如 HikariCP)return new HikariDataSource();}
        }
        
      • 用戶自定義覆蓋: 如果用戶在自己的配置類中定義了相同類型的 Bean:

        @Configuration
        public class MyCustomConfig {@Beanpublic DataSource myCustomDataSource() { // 自定義 DataSourcereturn new MySpecialDataSource();}
        }
        

        此時,容器中已經存在一個類型為 DataSource 的 Bean (myCustomDataSource)。因此,自動配置類中帶有 @ConditionalOnMissingBeandataSource() 方法將不會執行,用戶自定義的 DataSource 生效。這就是用戶覆蓋 Spring Boot 默認配置的方式。

      • 避免重復定義: 確保只有在用戶沒有提供特定 Bean 時才提供默認實現。

總結

  1. 啟動入口與 @SpringBootApplication
    • 應用啟動始于標注了 @SpringBootApplication 的主類。
    • @SpringBootApplication 是一個復合注解,核心包含:
      • @SpringBootConfiguration: 標記該類為配置類(本質上是 @Configuration)。
      • @ComponentScan: 掃描主類所在包及其子包下的 @Component, @Service, @Repository, @Controller 等注解的類,將其注冊為 Bean。
      • @EnableAutoConfiguration: 這是觸發自動配置的關鍵注解
  2. @EnableAutoConfiguration 的魔力
    • 這個注解利用 Spring Framework 的 @Import 機制導入了 AutoConfigurationImportSelector 類。
    • AutoConfigurationImportSelector 實現了 DeferredImportSelector 接口,它的核心方法是 selectImports()
  3. 加載自動配置候選列表 (spring.factories / AutoConfiguration.imports)
    • AutoConfigurationImportSelector.selectImports() 方法的核心任務是從 classpath 下的特定位置加載所有可能應用的自動配置類的全限定名列表
    • 加載位置 (優先級從上到下):
      1. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 2.7+ 推薦方式,一行一個全類名)。
      2. META-INF/spring.factories (舊方式,但廣泛支持,EnableAutoConfiguration key 下列出全類名)。(spring 3.x版本后沒有了)
    • Spring Boot 在 spring-boot-autoconfigure jar 包的 META-INF 目錄下提供了大量內置的自動配置類(如 DataSourceAutoConfiguration, WebMvcAutoConfiguration, JacksonAutoConfiguration 等)。
    • 第三方 starter 也會在自己的 jar 包中提供相應的 spring.factoriesAutoConfiguration.imports 文件來聲明其自動配置類。
  4. 自動配置類的過濾與篩選 (條件注解 @Conditional)
    • 加載得到的候選自動配置類列表并不會全部生效。Spring Boot 的核心機制在于按需配置
    • 每個自動配置類上都標注了一個或多個 @Conditional 及其衍生注解(條件注解),用來判斷這個配置類是否應該被加載、其內部的 Bean 是否應該被創建。
    • 關鍵條件注解舉例:
      • @ConditionalOnClass: 當 classpath 下存在指定的類時才生效。
      • @ConditionalOnMissingClass: 當 classpath 下不存在指定的類時才生效。
      • @ConditionalOnBean: 當 Spring 容器中存在指定的 Bean 時才生效。
      • @ConditionalOnMissingBean: 當 Spring 容器中不存在指定的 Bean 時才生效(這是用戶覆蓋默認配置的關鍵)。
      • @ConditionalOnProperty: 當指定的配置屬性具有特定值時才生效。
      • @ConditionalOnResource: 當 classpath 下存在指定的資源文件時才生效。
      • @ConditionalOnWebApplication / @ConditionalOnNotWebApplication: 根據應用是否是 Web 應用來決定。
      • @ConditionalOnJava: 根據運行時的 Java 版本決定。
    • 條件評估過程:
      • Spring Boot 在啟動時(具體在 ConfigurationClassPostProcessor 處理配置類期間)會創建一個 ConditionEvaluator
      • 對于每個候選的自動配置類,ConditionEvaluator解析并評估其上的所有條件注解
      • 只有所有條件都滿足的自動配置類,才會被真正導入 Spring 應用上下文,其內部的 @Bean 方法才有機會被執行。
  5. 自動配置類的執行與 Bean 注冊
    • 通過條件評估篩選出來的自動配置類,會被當作標準的 Spring @Configuration 類處理。
    • Spring 容器會解析這些配置類:
      • 執行類內部的 @Bean 方法,將返回的對象注冊為 Spring 容器中的 Bean。
      • 處理類上的其他注解(如 @ImportResource, @PropertySource 等)。
    • 自動配置類的作用:
      • 創建和配置應用運行所需的核心基礎設施 Bean(如 DataSource, JdbcTemplate, DispatcherServlet, ObjectMapper, RestTemplateBuilder 等)。
      • 根據 classpath 存在的依賴和用戶配置的屬性,智能地配置這些 Bean 的行為和默認值。
      • 提供默認的屬性綁定(通過 @EnableConfigurationProperties + @ConfigurationProperties)。
  6. 用戶配置優先
    • 核心原則:自動配置是非侵入式的。用戶定義的 Bean 總是優先于自動配置提供的默認 Bean
    • 這是通過 @ConditionalOnMissingBean 注解實現的。自動配置類在定義其默認 Bean 時,通常會加上 @ConditionalOnMissingBean
    • 如果用戶在自己的 @Configuration 類中顯式定義了一個相同類型的 Bean,那么這個用戶定義的 Bean 會被注冊到容器中。
    • 當自動配置類執行到定義該默認 Bean 的方法時,@ConditionalOnMissingBean 條件檢測到容器中已存在該類型的 Bean(用戶定義的),條件不滿足,該方法不會執行,從而避免了注冊默認 Bean。用戶配置成功覆蓋了自動配置。
  7. 屬性配置 (application.properties/yml)
    • 自動配置類大量使用 @ConfigurationProperties 來綁定外部配置(主要是 application.propertiesapplication.yml 文件)。
    • 這些屬性用于覆蓋自動配置提供的默認值。例如,配置 spring.datasource.url, spring.datasource.username, spring.datasource.password 來覆蓋 DataSourceAutoConfiguration 創建數據源時使用的默認值。

(三)自定義starter

自定義Starter是一種封裝特定功能(如自動配置、依賴管理等)的便捷方式,讓其他項目能快速集成。

在這里插入圖片描述

自定義JWT Starter實現

這里以JWT為例,講解如何實現jwt starter如何實現,通過實現自定義stater可以更好理解起步依賴和自動配置。我們需要實現一個jwt-spring-boot-starter自定義starter,在其他模塊導入這個依賴就可以直接調用JwtUtils工具類里面的方法。

jwt-starter/
├── jwt-spring-boot-autoconfigure/  // 自動配置模塊
│   ├── src/main/java
│   │   └── com/example/jwt
│   │       ├── JwtAutoConfiguration.java
│   │       ├── JwtProperties.java
│   │       └── JwtUtils.java
│   │ 
│   └── pom.xml
├── jwt-spring-boot-starter/        // Starter模塊
│   └── pom.xml
  1. 創建jwt-spring-boot-autoconfigure模塊,只留下pom.xml文件和scr文件,刪除主啟動類,跟上面結構一樣(需要留下resource目錄),并導入依賴

    <!-- jjwt 核心 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><!-- 實現包 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><!-- 使用 jackson 解析 JSON --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope></dependency>
    
  2. 創建jwt-spring-boot-starter模塊,只留下pom.xml文件,再導入jwt-spring-boot-autoconfigure模塊依賴

    <dependency><groupId>com.example</groupId><artifactId>jwt-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  3. jwt-spring-boot-autoconfigure模塊創建JwtProperties配置類

    package com.example.jwt;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "jwt")
    public class JwtProperties {// 密鑰(Base64編碼)private String secret = "c2VjcmV0LWtleS1mb3Itand0LWF1dGgtc3RhcnRlci1leGFtcGxl";// 有效時間(秒),默認1小時private long expiration = 3600;// 簽發者private String issuer = "jwt-starter";// 是否開啟調試模式private boolean debug = false;// Getters and Setterspublic String getSecret() {return secret;}public void setSecret(String secret) {this.secret = secret;}public long getExpiration() {return expiration;}public void setExpiration(long expiration) {this.expiration = expiration;}public String getIssuer() {return issuer;}public void setIssuer(String issuer) {this.issuer = issuer;}public boolean isDebug() {return debug;}public void setDebug(boolean debug) {this.debug = debug;}
    }
    
  4. 然后創建JwtUtils工具類

    package com.example;import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import io.jsonwebtoken.io.Decoders;
    import io.jsonwebtoken.security.Keys;import java.security.Key;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;public class JwtUtils {private final JwtProperties jwtProperties;public JwtUtils(JwtProperties jwtProperties) {this.jwtProperties = jwtProperties;}// 生成JWT Tokenpublic String generateToken(String subject) {return generateToken(subject, new HashMap<>());}public String generateToken(String subject, Map<String, Object> claims) {long now = System.currentTimeMillis();return Jwts.builder().setClaims(claims).setSubject(subject).setIssuer(jwtProperties.getIssuer()).setIssuedAt(new Date(now)).setExpiration(new Date(now + jwtProperties.getExpiration() * 1000)).signWith(getSignKey(), SignatureAlgorithm.HS256).compact();}// 解析JWT Tokenpublic Claims parseToken(String token) {return Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token).getBody();}// 驗證Token是否有效public boolean validateToken(String token) {try {parseToken(token);return true;} catch (Exception e) {if (jwtProperties.isDebug()) {e.printStackTrace();}return false;}}// 從配置的密鑰獲取簽名Keyprivate Key getSignKey() {byte[] keyBytes = Decoders.BASE64.decode(jwtProperties.getSecret());return Keys.hmacShaKeyFor(keyBytes);}// 獲取Token中的主題(通常是用戶ID)public String getSubjectFromToken(String token) {Claims claims = parseToken(token);return claims.getSubject();}// 獲取Token中的自定義聲明public Object getClaimFromToken(String token, String claimName) {Claims claims = parseToken(token);return claims.get(claimName);}
    }
    
  5. 創建JwtAutoConfiguration自動配置的配置類

    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;@Configuration
    // 啟用并注冊配置屬性類
    // application.yml 或 application.properties 中的配置項自動綁定到一個 Java Bean 類中。
    @EnableConfigurationProperties(JwtProperties.class)
    public class JwtAutoConfiguration {// 當spring容器不存在這個bean時,才注入該Bean@ConditionalOnMissingBean@Beanpublic JwtUtils jwtUtils (JwtProperties jwtProperties){return new JwtUtils(jwtProperties);}
    }
    
  6. 創建目錄文件src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports導入自己的JwtAutoConfiguration全路徑

    com.example.JwtAutoConfiguration  
    
  7. 然后是測試,另外新建一個模塊,導入我們剛才寫好的依賴

    <dependency><groupId>com.example</groupId><artifactId>jwt-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version>
    </dependency>
    
    @SpringBootTest
    public class TestLogin {@Autowiredprivate JwtUtils jwtUtils;@Testvoid JwtStarterTest (){HashMap<String, Object> map = new HashMap<>();map.put("username","tom");map.put("password","123123");String jwt = jwtUtils.generateToken("0001",map);System.out.println(jwt);}
    }
    

最后這里可以看見jwt-spring-boot-starter生效了

在這里插入圖片描述

通過本文的講解,相信你已經對 Spring Boot 的兩大核心機制 —— 起步依賴(Starter) 和 自動裝配(Auto-Configuration) 有了更深入的理解。

Spring Boot 的“開箱即用”并非魔法,而是建立在 Spring 強大的容器管理和條件化配置基礎上的工程智慧。理解這些底層原理,不僅能幫助你更好地使用 Spring Boot,還能在遇到復雜問題時更快地定位和解決。

如果你覺得這篇文章對你有幫助,歡迎點贊、收藏并分享給更多需要的小伙伴。這里是 盛碼筆記,我們下期再見!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89116.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89116.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89116.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據結構:棧(區間問題)

碼蹄集OJ-小碼哥的棧 #include<bits/stdc.h> using namespace std; #define int long long const int N1e67; struct MOOE {int ll,rr; }; stack<MOOE>st; signed main( ) {ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin>>n;while(n--){int opt…

Vue 中 data、watch、created 和 methods

以下是 Vue 中 data、watch、created 和 methods 的詳細解釋&#xff0c;結合常見使用場景和示例&#xff1a;1. data&#xff1a;響應式數據容器 作用&#xff1a;定義組件的響應式數據&#xff08;狀態&#xff09;&#xff0c;當數據變化時&#xff0c;視圖自動更新。特點&a…

精密模具冷卻孔內輪廓檢測方法探究 —— 激光頻率梳 3D 輪廓檢測

引言精密模具冷卻孔的內輪廓精度直接影響注塑成型效率與制品質量。冷卻孔具有深徑比大&#xff08;可達 25:1&#xff09;、結構復雜&#xff08;多為螺旋形或異形&#xff09;、表面質量要求高&#xff08;Ra≤0.2μm&#xff09;等特點&#xff0c;傳統檢測方法難以滿足其高精…

Vue單文件組件與腳手架工程化開發

一、Vue與VueComponent原型關系解析1. 原型鏈關系圖解在Vue中&#xff0c;組件實例(VueComponent)與Vue實例之間存在特殊的原型鏈關系&#xff1a;VueComponent.prototype.__proto__ Vue.prototype這種設計使得所有組件都能訪問Vue原型上定義的方法和屬性。2. 原理驗證示例// …

架構設計之計算高性能——單體服務器高性能

架構設計之計算高性能——單體服務器高性能 高性能是每個程序員共同的追求&#xff0c;無論是開發系統&#xff0c;還是僅僅只是寫一段腳本&#xff0c;都希望能夠達到高性能的效果&#xff0c;而高性能又是軟件系統設計中最復雜的一步。無論是開發千萬級并發的電商系統&#…

Unity燈光面板環境設置

在Unity中&#xff0c;環境設置&#xff08;Environment Lighting&#xff09; 是燈光面板&#xff08;Lighting Window&#xff09;的核心功能之一&#xff0c;用于控制場景的全局光照效果&#xff0c;包括天空盒、環境光、反射和霧效等。這些設置直接影響場景的整體氛圍和真實…

MySQL語句優化案例

1.案例in查詢條件很慢其中in中共115個select id,detail_id,request,response,utime,ctime from response_detaill where detaill_id in (26371986, 26372242, 26371984, 26371990, 26400150, 26371988, 26371994, 26371992,26371998, 26371996, 26371970, 26371968, 2637197…

能行為監測算法:低成本下的高效管理

AI監控智慧公司管理&#xff1a;降本增效的實踐與突破一、背景&#xff1a;經濟壓力下的管理轉型需求在經濟下行周期&#xff0c;企業面臨人力成本攀升、管理效率低下、安全風險頻發等多重挑戰。傳統監控依賴人工巡檢&#xff0c;存在響應滯后、誤判率高、數據孤島等問題&#…

當前(2024-07-14)視頻插幀(VFI)方向的 SOTA 基本被三篇頂會工作占據,按“精度-速度-感知質量”三條線總結如下,供你快速定位最新范式

當前&#xff08;2024-07-14&#xff09;視頻插幀&#xff08;VFI&#xff09;方向的 SOTA 基本被三篇頂會工作占據&#xff0c;按“精度-速度-感知質量”三條線總結如下&#xff0c;供你快速定位最新范式。感知質量最佳&#xff1a;CVPR 2024 ? PerVFI ? 關鍵詞&#xff1a;…

開源 python 應用 開發(七)數據可視化

最近有個項目需要做視覺自動化處理的工具&#xff0c;最后選用的軟件為python&#xff0c;剛好這個機會進行系統學習。短時間學習&#xff0c;需要快速開發&#xff0c;所以記錄要點步驟&#xff0c;防止忘記。 鏈接&#xff1a; 開源 python 應用 開發&#xff08;一&#xf…

基于深度學習的情感分析模型:從文本數據到模型部署

前言 情感分析&#xff08;Sentiment Analysis&#xff09;是自然語言處理&#xff08;NLP&#xff09;領域中的一個重要應用&#xff0c;它通過分析文本數據來判斷文本的情感傾向&#xff0c;例如正面、負面或中性。隨著社交媒體的興起&#xff0c;情感分析在市場調研、品牌管…

使用python 實現一個http server

下面是一個使用 Python 內置庫 http.server 的簡單 HTTP 服務器實現。不需要安裝任何第三方庫&#xff0c;非常適合做演示或開發測試用。 from http.server import HTTPServer, BaseHTTPRequestHandlerclass SimpleHTTPRequestHandler(BaseHTTPRequestHandler):def do_GET(self…

Redis技術筆記-主從復制、哨兵與持久化實戰指南

目錄 前言 一、Redis主從復制 &#xff08;一&#xff09;Redis主從復制介紹 &#xff08;二&#xff09;基本環境準備 &#xff08;三&#xff09;工作原理 &#xff08;四&#xff09;結構模式 &#xff08;五&#xff09;一主一從&#xff08;無密碼&#xff09; 配置…

sundog公司的SilverLining SDK庫實現3d動態云層和下雨、下雨、雨夾雪效果

OSG系列文章目錄 文章目錄OSG系列文章目錄前言一、3d動態云與下雨、下雪效果不能同時出現二、3d動態云與下雨、下雪效果不能同時出現的原因三、解決辦法&#xff1a;前言 先看下效果&#xff1a;下雨 效果&#xff1a;下雪 效果&#xff1a;雨夾雪 &#x1f324;? Sundo…

Python:簡易的 TCP 服務端與客戶端示例

下面是一個完整的 TCP 服務端與客戶端示例&#xff0c;適用于 Python 3&#xff0c;使用 socket 模塊&#xff0c;并正確處理了中文傳輸與異常情況&#xff0c;支持基本的多輪通信。TCP 服務端&#xff08;server_tcp.py&#xff09;import socket HOST 127.0.0.1 # 監聽本地…

文心一言 4.5 開源深度剖析:中文霸主登場,開源引擎重塑大模型生態

> 百度用一場徹底的開源風暴,宣告中文大模型進入性能與普惠并重的新紀元——這里沒有技術黑箱,只有開發者手中躍動的創新火花。 2025年,當全球大模型競賽進入深水區,百度文心一言4.5的開源如同一顆重磅炸彈,徹底打破了“閉源即領先”的固有認知。這一次,中國團隊不…

解決“Windows 無法啟動服務”問題指南

錯誤1067&#xff1a;進程意外終止一、重啟計算機有時系統出現臨時性的服務故障&#xff0c;重啟計算機就可以有效解決問題。需要注意的是&#xff0c;在重啟之前&#xff0c;需要保存好所有未保存的工作&#xff0c;以免數據丟失。重啟完成后&#xff0c;再次嘗試啟動相關服務…

銀河麒麟(Kylin) - V10 GFB高級服務器操作系統ARM64部署昇騰910b訓練機以及Docker安裝

銀河麒麟(Kylin) - V10 GFB高級服務器操作系統ARM64部署昇騰910b訓練機以及Docker安裝 原因 項目需要使用Deepseek-r1-distill-qwen-32b來做訓練&#xff0c;在此記錄 測試環境 服務器配置 型號&#xff1a;G5680V2 CPU&#xff1a;CPU 4Kunpeng 920-5250 NPU&#xff1a;NP…

消息中間件(Kafka VS RocketMQ)

目錄 一、概要介紹 二、架構與原理 三、消費模式 1、Kafka—純拉模式 2、RocketMQ—拉模式 3、RocketMQ—推模式 4、模式對比 四、特殊消息 1、順序消息 2、消息過濾 3、延遲消息 4、事務消息 5、廣播消息 五、高吞吐 六、高可用 七、高可靠 一、概要介紹 Apa…

MyBatis級聯查詢深度解析:一對多關聯實戰指南

MyBatis級聯查詢深度解析&#xff1a;一對多關聯實戰指南在實際企業級開發中&#xff0c;單表操作僅占20%的場景&#xff0c;而80%的業務需求涉及多表關聯查詢。本文將以一對多關系為例&#xff0c;深入剖析MyBatis級聯查詢的實現原理與最佳實踐&#xff0c;助你掌握高效的數據…