最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot啟動(dòng)加載流程自動(dòng)配置的底層原理(專家解讀)

 更新時(shí)間:2026年01月02日 11:56:46   作者:張彥峰ZYF  
Spring Boot這么少的配置卻能實(shí)現(xiàn)如此豐富的功能?為何啟動(dòng)一個(gè)Spring Boot應(yīng)用可以變得如此簡單便捷?在這個(gè)看似平凡的啟動(dòng)過程中,其實(shí)隱藏著一套精妙的自動(dòng)化機(jī)制和源碼邏輯,本文將帶你深入探索 Spring Boot 的啟動(dòng)流程,逐步揭開自動(dòng)配置的幕后奧秘

在使用 Spring Boot 時(shí),開發(fā)者常常會(huì)感到驚訝:為什么這么少的配置卻能實(shí)現(xiàn)如此豐富的功能?為何啟動(dòng)一個(gè) Spring Boot 應(yīng)用可以變得如此簡單便捷?在這個(gè)看似平凡的啟動(dòng)過程中,其實(shí)隱藏著一套精妙的自動(dòng)化機(jī)制和源碼邏輯。本文將帶你深入探索 Spring Boot 的啟動(dòng)流程,逐步揭開自動(dòng)配置的幕后奧秘。通過了解這些底層原理,你將發(fā)現(xiàn),原來每一次應(yīng)用的無縫啟動(dòng)都是精心設(shè)計(jì)的結(jié)果,讓你對(duì) Spring Boot 有更深刻的理解和掌控力!

一、整體概述

(一)基本整體初步分析

Spring Boot 是一個(gè)用于構(gòu)建獨(dú)立的、生產(chǎn)級(jí)的 Spring 應(yīng)用程序的框架,它提供了自動(dòng)化的配置和約定優(yōu)于配置的原則。在理解 Spring Boot 的啟動(dòng)配置原理之前,我們需要了解幾個(gè)關(guān)鍵概念。

首先,Spring Boot 使用了基于約定的自動(dòng)配置機(jī)制。它通過在 classpath 下查找特定的配置文件和類,根據(jù)應(yīng)用程序所使用的依賴自動(dòng)配置 Spring 應(yīng)用程序的各種組件。這樣可以大大簡化開發(fā)者的工作,減少了手動(dòng)配置的需求。

其次,Spring Boot 使用了條件化配置(Conditional Configuration)的機(jī)制。這意味著配置的應(yīng)用取決于一組條件是否滿足。條件可以基于多種因素,如 classpath 中存在特定的類、特定的 bean 是否存在等等。通過條件化配置,Spring Boot 可以根據(jù)不同的環(huán)境和需求進(jìn)行動(dòng)態(tài)的配置。

Spring Boot 的啟動(dòng)配置原理可以概括如下:

  1. 在啟動(dòng)過程中,Spring Boot 會(huì)加載并解析應(yīng)用程序的配置文件,其中包括 application.properties 或 application.yml 文件等。這些文件中可以定義各種屬性和配置信息,如數(shù)據(jù)庫連接、日志級(jí)別等。
  2. Spring Boot 會(huì)自動(dòng)掃描 classpath 下的特定包,尋找?guī)в刑囟ㄗ⒔獾念?,?@SpringBootApplication。這個(gè)注解標(biāo)識(shí)了一個(gè) Spring Boot 應(yīng)用程序的入口點(diǎn)。
  3. 根據(jù)配置文件中的屬性和條件化配置的機(jī)制,Spring Boot 自動(dòng)配置應(yīng)用程序的各種組件,包括數(shù)據(jù)庫連接池、消息隊(duì)列、Web 服務(wù)器等。如果需要進(jìn)行自定義配置,可以使用專門的注解或編寫自定義的配置類。
  4. 在應(yīng)用程序啟動(dòng)時(shí),Spring Boot 會(huì)初始化 Spring 容器,并根據(jù)配置進(jìn)行相應(yīng)的初始化工作。這包括創(chuàng)建和管理 bean、處理依賴注入等。

總的來說,Spring Boot 的啟動(dòng)配置原理是基于自動(dòng)化的約定和條件化配置機(jī)制。它通過讀取配置文件、掃描注解、自動(dòng)配置組件等步驟,簡化了應(yīng)用程序的配置過程,并提供了靈活性和易用性。

(二)從啟動(dòng)來看整體過程圖分析

每個(gè)SpringBoot程序都有一個(gè)主入口main方法,main里面調(diào)用SpringApplication.run()啟動(dòng)整個(gè)SpringBoot程序,該方法所在類需要使用@SpringBootApplication注解,例如如下

package org.zyf.javabasic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * 描述:啟動(dòng)入口類
 *
 * @author yanfengzhang
 * @date 2019-12-19 18:11
 */
@SpringBootApplication
@ComponentScan(basePackages = {"org.zyf.javabasic"})
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableSwagger2
public class ZYFApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ZYFApplication.class, args);
}

其中對(duì)@SpringBootApplication進(jìn)行展開分析:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootApplication包括三個(gè)注解,功能如下:

  • @SpringBootConfiguration(內(nèi)部為@Configuration):被標(biāo)注的類等于在spring的XML配置文件中(applicationContext.xml),裝配所有bean事務(wù),提供了一個(gè)spring的上下文環(huán)境
  • @ComponentScan:組件掃描,可自動(dòng)發(fā)現(xiàn)和裝配Bean(比如@Component和@Configuration),默認(rèn)掃描SpringApplication的run方法里類所在的包路徑下所有文件
  • @EnableAutoConfiguration:激活SpringBoot自動(dòng)裝配的特性

現(xiàn)在對(duì)主入口main方法進(jìn)行展開來給出整體的流程圖分析

二、SpringApplication構(gòu)造過程分析

進(jìn)入main里面的run方法,創(chuàng)建了一個(gè)SpringApplication實(shí)例,配置一些基本的環(huán)境變量、資源、構(gòu)造器、監(jiān)聽器,進(jìn)入這個(gè)SpringApplication有參構(gòu)造函數(shù)

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

注意:在該構(gòu)造方法內(nèi),做的工作就是把相關(guān)的類(主要是initializer和listener)加載進(jìn)容器中,并沒有執(zhí)行

(一)驗(yàn)證主配置類不為空并且保存主類

(二)推斷項(xiàng)目的類型

進(jìn)入對(duì)應(yīng)方法分析如下:

推斷項(xiàng)目的類型可能為reactive、none、servlet三種類型,默認(rèn)為servlet類型。其使用類加載器判斷類型的邏輯如下

類型

判斷情況

reactive

存在Spring WebFlux的DispatcherHandler存在,但是Spring MVC的DispatcherServlet不存在

none

二者都不存在

servlet

剩余所有情況

(三)初始化initializers

先進(jìn)入ApplicationContextInitializer接口,這個(gè)接口只有一個(gè)方法initialize

package org.springframework.context;

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}

查看實(shí)現(xiàn)了該這個(gè)接口的類進(jìn)行分析

以SharedMetadataReaderFactoryContextInitializer實(shí)現(xiàn)類為例,跳轉(zhuǎn)對(duì)應(yīng)jar包,可以看到里面 spring.factories就在key為ApplicationContextInitializer中指定了對(duì)應(yīng)的實(shí)現(xiàn)類,例如:

進(jìn)入SharedMetadataReaderFactoryContextInitializer,其實(shí)現(xiàn)了ApplicationContextInitializer接口,并實(shí)現(xiàn)了里面的initialize方法

現(xiàn)在回到一開始再來看getSpringFactoriesInstance()方法,其核心為loadFactoryNames()方法

進(jìn)入loadFactoryNames()方法,可以看到ApplicationContextInitializer的相關(guān)獲取內(nèi)容直接就是從文件“META-INF/spring.factories”中獲取保存的

(四)加載相關(guān)的listeners

先進(jìn)入ApplicationListener接口,這個(gè)接口只有一個(gè)方法onApplicationEvent

package org.springframework.context;

import java.util.EventListener;

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

查看實(shí)現(xiàn)了該這個(gè)接口的類進(jìn)行分析

以BackgroundPreinitializer實(shí)現(xiàn)類為例,跳轉(zhuǎn)對(duì)應(yīng)jar包,可以看到里面 spring.factories就在key為ApplicationListener中指定了對(duì)應(yīng)的實(shí)現(xiàn)類:

 進(jìn)入BackgroundPreinitializer,其實(shí)現(xiàn)了ApplicationListener,并實(shí)現(xiàn)了里面的onApplicationEvent

方法如下:

現(xiàn)在回到一開始再來看getSpringFactoriesInstance()方法,其處理流程和上面的一樣,即依然是從類路徑下找到META-INF/spring.factories配置的所有ApplicationListener

(五)決定ApplicationClass主程序

進(jìn)入deduceMainApplicationClass方法

三、SpringApplication啟動(dòng)過程分析

SpringBoot啟動(dòng)方案,包括啟動(dòng)流程的監(jiān)聽模塊、加載配置環(huán)境模塊、及核心的創(chuàng)建上下文環(huán)境模塊以及后續(xù)的收尾回調(diào)等內(nèi)容

(一)監(jiān)控器監(jiān)聽容器啟動(dòng)并進(jìn)行圖形化頁面處理

(二)監(jiān)聽器SpringApplicationRunListeners開啟監(jiān)聽

直接開始分析ApringApplicationRunListeners,內(nèi)含SpringApplicationRunListener的集合,其中starting方法就是對(duì)listeners進(jìn)行遍歷,對(duì)每個(gè)listener都調(diào)用starting方法

SpringApplicationRunListener和ApplicationListener都是SpringBoot中的事件監(jiān)聽器,但是它們所監(jiān)聽的事件和觸發(fā)時(shí)機(jī)有所不同,其區(qū)別如下:

  1. 監(jiān)聽的事件不同
    SpringApplicationRunListener主要監(jiān)聽SpringApplication運(yùn)行時(shí)的各種事件,例如應(yīng)用程序開始啟動(dòng)、應(yīng)用程序啟動(dòng)失敗、應(yīng)用程序啟動(dòng)完成等事件。而ApplicationListener主要監(jiān)聽Spring容器中的各種事件,例如Bean加載完成、上下文刷新完成等事件。
  2. 觸發(fā)時(shí)機(jī)不同
    SpringApplicationRunListener在SpringApplication啟動(dòng)時(shí)就開始工作,可以接收到應(yīng)用程序開始啟動(dòng)、應(yīng)用程序啟動(dòng)失敗、應(yīng)用程序啟動(dòng)成功等各種事件。而ApplicationListener則是在Spring容器啟動(dòng)完成后,才能開始工作,監(jiān)聽的是Spring容器中的各種事件。
  3. 使用場景不同
    在實(shí)際應(yīng)用中,SpringApplicationRunListener主要用于監(jiān)聽SpringApplication的啟動(dòng)過程,例如在應(yīng)用程序啟動(dòng)前后執(zhí)行某些操作、監(jiān)聽?wèi)?yīng)用程序啟動(dòng)失敗事件并做出相應(yīng)的操作等。而ApplicationListener則用于監(jiān)聽Spring容器中的各種事件,例如在Bean加載完成后做出相應(yīng)的操作、在上下文刷新完成后更新一些狀態(tài)等。

總之,盡管SpringApplicationRunListener和ApplicationListener都是SpringBoot中的事件監(jiān)聽器,但是它們所監(jiān)聽的事件、觸發(fā)時(shí)機(jī)、使用場景等都有所不同,我們需要根據(jù)具體的應(yīng)用需求,選擇合適的監(jiān)聽器來完成應(yīng)用程序的事件處理。

在進(jìn)入看一下SpringApplicationRunListener這個(gè)類

package org.springframework.boot;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

對(duì)應(yīng)方法說明如下

監(jiān)聽方法

運(yùn)行階段說明

SpringBoot起始版本

contextLoaded(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext完成加載,但仍未啟動(dòng);通知監(jiān)聽器,ApplicationContext已經(jīng)完成IoC配置

1.0

contextPrepared(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext準(zhǔn)備妥當(dāng):通知監(jiān)聽器,ApplicationContext已經(jīng)創(chuàng)建并初始化完成

1.0

environmentPrepared(ConfigurationEnvironment)

ConfigurationEnvironment準(zhǔn)備妥當(dāng),允許將其調(diào)整

1.0

failed(ConfigurationApplicationEnvironment,Throwable)

Spring應(yīng)用運(yùn)行失敗

2.0

running(ConfigurationApplicationEnvironment)

Spring應(yīng)用正在運(yùn)行

2.0

started(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext已啟動(dòng),此時(shí)SpringBean已初始化完成

2.0

starting()

run方法執(zhí)行的時(shí)候立馬執(zhí)行:通知監(jiān)聽器,SpringBoot開始執(zhí)行

1.0

總的來說就是創(chuàng)建了應(yīng)用的監(jiān)聽器SpringApplicationRunListeners并調(diào)用start()開始監(jiān)聽,先在getRunListeners中獲取了所有的監(jiān)聽器,然后starting開啟

(三)environmentPrepared環(huán)境準(zhǔn)備處理

分析這段代碼,進(jìn)入準(zhǔn)備環(huán)境的方法prepareEnvironment中,可以看到如下:

首先創(chuàng)建了一個(gè)環(huán)境ConfigurationEnvironment(有的話就獲取,沒有則創(chuàng)建)

回到該方法返回的地方,通過this.configureEnvironment對(duì)環(huán)境進(jìn)行設(shè)置,接著如下:

環(huán)境配置好以后回調(diào)了SpringApplicationRunListener的environmentPrepared函數(shù),進(jìn)入該方法:

可以看到environmentPrepared環(huán)境準(zhǔn)備中進(jìn)行了通知監(jiān)聽器,Environment準(zhǔn)備完成。

回到開始處,可以看到環(huán)境準(zhǔn)備完成后通過bindToSpringApplication將環(huán)境綁定到程序中。

(四)banner打印

打印對(duì)應(yīng)的banner信息

也就是啟動(dòng)后的改部分,見如下

實(shí)際中,改圖可以進(jìn)行替換,只需要在resources增加banner.txt信息即可,例如如下:

其中的原理可以繼續(xù)點(diǎn)開對(duì)應(yīng)后續(xù)代碼分析,這個(gè)后續(xù)中講解替換思路。

(五)創(chuàng)建Spring應(yīng)用上下文

創(chuàng)建應(yīng)用上下文即IOC過程,IOC容器是驅(qū)動(dòng)整體SpringBoot應(yīng)用組件的核心,進(jìn)入該方法:

這里的創(chuàng)建是根據(jù)SpringApplication在構(gòu)造階段所推斷的web應(yīng)用類型進(jìn)行IOC容器的創(chuàng)建,IOC容器就是run返回的內(nèi)容。

(六)Spring應(yīng)用上下文準(zhǔn)備階段

prepareContext方法將listeners、environment、applicationArguments、banner等重要組件與上下文對(duì)象關(guān)聯(lián),對(duì)上下文對(duì)象進(jìn)行進(jìn)一步配置,進(jìn)入該方法具體分析:

可以看到首先分別保存了剛才生成的environment、ApplicationContext,接下來的applyInitializers方法是執(zhí)行初始化,進(jìn)入該方法

方法內(nèi)部遍歷所有的initializer,然后依次回調(diào)里面所有的initialize方法(這些initializer就是在springboot剛啟動(dòng)時(shí)構(gòu)造new springApplication時(shí)添加的),設(shè)置完當(dāng)前環(huán)境并完成初始化之后,回調(diào)了所有Linsteners的contextPrepared方法

接下來將命令行參數(shù)和banner注冊(cè)到IOC容器來,如下:

最后全部操作都完成后,這個(gè)方法回調(diào)了Listeners的contextLoaded方法,如上。

(七)Spring應(yīng)用上下文刷新階段

在該方法中首先注冊(cè)了一個(gè)shutdownHook線程,用來實(shí)現(xiàn)SpringBean銷毀生命周期回調(diào)

在執(zhí)行完refresh之后的控制臺(tái),可以看到tomcat和一些IOC容器的bean都被加載進(jìn)去了

(八)Spring應(yīng)用上下文收尾階段

其中afrerRefresh()并無內(nèi)容處理,后續(xù)的版本中已經(jīng)沒有改方法了

protected void afterRefresh(ConfigurableApplicationContext context,
 ApplicationArguments args) {
    }

收尾計(jì)時(shí)器停止,同時(shí)調(diào)用監(jiān)聽器的started()。

(九)回調(diào)工作處理

進(jìn)入該方法分析

ApplicationContext就是IOC容器,這個(gè)方法從IOC容器中獲取了所有的ApplicationRunner和ConmmandLineRunner,接下來進(jìn)行遍歷和回調(diào)。

在callRunners使用的這兩個(gè)類幾乎可以等價(jià),都是用于做一些客戶自定義的工作,而且是整個(gè)流程完成之后才會(huì)調(diào)用用戶自己定義的實(shí)現(xiàn)類的run方法,這兩個(gè)run方法的實(shí)現(xiàn)方法都是在容器基本初始化好的時(shí)候調(diào)用的。

緊接著,如果無異常代碼執(zhí)行如下:

監(jiān)聽器回調(diào)running()方法代表SpringApplication正常啟動(dòng)結(jié)束。

(十)SpringApplication啟動(dòng)異常處理

發(fā)生異常主要是對(duì)異常的處理,我們進(jìn)入該方法分析

可以看到這個(gè)異常報(bào)告類也是支持自定義并且自動(dòng)配置的,配置結(jié)束后,Springboot做了一些基本的收尾工作,返回了應(yīng)用環(huán)境上下文(IOC容器)。

四、SpringBoot自動(dòng)配置分析

Spring Boot 的自動(dòng)化配置模塊是該框架的核心功能之一,它可以大大簡化應(yīng)用程序的配置工作。下面是對(duì) Spring Boot 自動(dòng)化配置模塊的講解和分析:

  1. 自動(dòng)化配置的原理:Spring Boot 的自動(dòng)化配置模塊基于約定優(yōu)于配置的原則。它通過在 classpath 下掃描依賴和配置,自動(dòng)配置應(yīng)用程序的各個(gè)組件。它使用條件化配置的機(jī)制,根據(jù)環(huán)境和條件自動(dòng)選擇適當(dāng)?shù)呐渲谩?/strong>
  2. 自動(dòng)配置的實(shí)現(xiàn)方式:Spring Boot 自動(dòng)化配置模塊使用了 @Conditional 注解和條件注解來實(shí)現(xiàn)條件化配置。這些注解可以根據(jù)一組條件來決定是否啟用某個(gè)配置。例如,@ConditionalOnClass 根據(jù) classpath 中是否存在指定的類來判斷是否啟用配置。
  3. 自動(dòng)配置的加載順序Spring Boot 的自動(dòng)配置是通過在 classpath 下的 META-INF/spring.factories 文件中定義的自動(dòng)配置類來實(shí)現(xiàn)的。這些自動(dòng)配置類會(huì)被自動(dòng)加載,并根據(jù)條件進(jìn)行初始化和配置。根據(jù)條件的不同,可以有多個(gè)自動(dòng)配置類被加載,它們會(huì)按照優(yōu)先級(jí)順序進(jìn)行配置。
  4. 自動(dòng)配置的自定義:Spring Boot 允許開發(fā)者對(duì)自動(dòng)配置進(jìn)行自定義。你可以使用 @Conditional 注解和條件注解來定義自定義的條件,從而影響自動(dòng)配置的行為。你還可以使用 @EnableAutoConfiguration 注解來控制自動(dòng)配置的啟用或禁用。
  5. 自動(dòng)配置的好處:Spring Boot 的自動(dòng)化配置模塊帶來了很多好處。它大大減少了手動(dòng)配置的工作量,提高了開發(fā)效率。它提供了合理的默認(rèn)配置,減少了錯(cuò)誤配置的風(fēng)險(xiǎn)。同時(shí),它的條件化配置機(jī)制使得應(yīng)用程序更具靈活性,能夠根據(jù)不同的環(huán)境和需求進(jìn)行動(dòng)態(tài)的配置。

總的來說,Spring Boot 的自動(dòng)化配置模塊是該框架的重要特性之一。它通過約定優(yōu)于配置的原則和條件化配置機(jī)制,實(shí)現(xiàn)了自動(dòng)加載和配置應(yīng)用程序的各個(gè)組件。這為開發(fā)者提供了便利和靈活性,并大大簡化了應(yīng)用程序的配置過程。

現(xiàn)在回到我們一開始的圖示分析,該配置模塊的主要使用到了SpringFactoriesLoader,即Spring工廠加載器,該對(duì)象提供了loadFactoryNames方法,入?yún)閒actoryClass和classLoader,即需要傳入工廠類名稱和對(duì)應(yīng)的類加載器,方法會(huì)根據(jù)指定的classLoader,加載該類加器搜索路徑下的指定文件,即spring.factories文件,傳入的工廠類為接口,獲取到這些實(shí)現(xiàn)類的類名后,loadFactoryNames方法返回類名集合,方法調(diào)用方得到這些集合后,再通過反射獲取這些類的類對(duì)象、構(gòu)造方法,最終生成實(shí)例。

(一)自動(dòng)裝配原理分析

從@SpringBootApplication中的@EnableAutoConfiguration注解中可以看到其import了一個(gè)自動(dòng)配置導(dǎo)入選擇器AutoConfigurationImportSelect

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

其類圖如下

可以發(fā)現(xiàn)其最終實(shí)現(xiàn)了ImportSelector(選擇器)和BeanClassLoaderAware(bean類加載器中間件),其中這個(gè)選擇器的作用就是導(dǎo)入組件。

所有自動(dòng)裝配的邏輯都是在AutoConfigurationImportSelector里面的selectImports方法中實(shí)現(xiàn)的

進(jìn)入getAutoConfigurationEntry()方法

進(jìn)入getCandidateConfigurations()獲取候選配置方法,可以看到核心的SpringFactoriesLoader的loadFactoryNames()方法

其中SpringFactoriesLoader是Spring Framework工廠機(jī)制的加載器,loadFactoryNames是其對(duì)應(yīng)的加載方法,進(jìn)入這個(gè)核心的loadFactoryNames方法中查看

該處加載原理如下:

  • 掃描所有jar包路徑下 META-INF/spring.factories,這里是通過類加載器生成對(duì)應(yīng)的url路徑
  • 把掃描到的內(nèi)容包裝成properties對(duì)象,并對(duì)這個(gè)對(duì)象的內(nèi)容進(jìn)行遍歷,返回map,map的key為接口的全類名,value為接口全部實(shí)現(xiàn)類列表(列表里元素去重,防止重復(fù)加載),這個(gè)value的信息后續(xù)會(huì)作為loadSpringFactories方法的返回值
  • 在上一步返回的map之中查找并返回指定類名映射的實(shí)現(xiàn)類全類名列表

再看剛才getCandidateConfiguration方法中的getSpringFactoriesLoaderFactoryClass方法,返回的就是EnableAutoConfiguration類

也就是說要從剛才properties中再獲取這個(gè)類對(duì)應(yīng)的值,把它們加到容器中。

選取mybatis-spring-boot-autoconfigure下的spring.factories文件分析一下:

每一個(gè) xxxAutoConfiguration類都是容器中的一個(gè)組件,都加入到容器中,用它們做自動(dòng)配置;

只有進(jìn)入到容器中,這些自動(dòng)配置類才會(huì)起作用——進(jìn)行自動(dòng)配置功能

(二)條件化自動(dòng)裝配

對(duì)于使用@Configuration的自動(dòng)配置類,其條件自動(dòng)化裝配以@condition為核心。在spring底層中的@conditional注解會(huì)根據(jù)不同的滿足條件生效整個(gè)配置類里面的配置。

條件化裝配可以分為以下幾類:

Class條件注解

注解

說明

@ConditionalOnClass

指定類存在時(shí)生效

@ConditionalOnMissingClass

指定類缺失時(shí)生效

Bean條件注解

注解

說明

@ConditionalOnBean

指定Bean存在時(shí)生效

@ConditionalOnMissingBean

指定Bean缺失時(shí)生效

屬性條件注解

注解

說明

@ConditionalOnProperty

使用屬性(application.properties)的值判斷是否生效

Web應(yīng)用條件注解

注解

說明

@ConditionalOnWebApplication

是web類型時(shí)生效

@ConditionalOnNotWebApplication

不是web類型時(shí)生效

其他條件注解

@Conditional擴(kuò)展注解

作用(判斷是否滿足當(dāng)前指定條件)

@ConditionalOnJava

系統(tǒng)的java版本是否符合要求

@ConditionalOnExpression

滿足SpEL表達(dá)式指定

@ConditionalOnSingleCandidate

容器中只有一個(gè)指定的Bean,或者這個(gè)Bean是首選Bean

@ConditionalOnResource

類路徑下是否存在指定資源文件

@ConditionalOnJndi

JNDI存在指定項(xiàng)

分析@ConditionalOnWebApplication

package org.springframework.boot.autoconfigure.condition;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
    ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;

    public static enum Type {
        ANY,
        SERVLET,
        REACTIVE;

        private Type() {
        }
    }
}

進(jìn)入OnWebApplicationCondition類,里面有一個(gè)getMatchOutcome()方法,就是判斷是否符合當(dāng)前配置條件

該方法首先判斷是否使用了這個(gè)注釋,然后使用isWebApplication來判斷當(dāng)前是否是web應(yīng)用,這些match的方法判斷指定的條件成立,才會(huì)給容器添加組件,配置內(nèi)容才會(huì)生效。

(三)自動(dòng)配置原理舉例:HttpEncodingAutoConfiguration(HTTP編碼自動(dòng)配置)

可以看到以下信息:

  • @Configuration 代表這是一個(gè)配置類,類似編寫的配置文件,也可以給容器中添加組件。
  • @EnableConfigurationProperties 啟用指定類的ConfigurationProperties功能,將配置文件application.properties的值和ServerProperties綁定起來,并把ServerProperties加入到IOC容器中。
  • @ConditionalOnClass用來判斷當(dāng)前項(xiàng)目是否含有這個(gè)類,這里CharacterEncodingFilter的作用就是springMVC亂碼解決的過濾器(以前在spring的xml文件中配置的),如果有這個(gè)過濾器則配置生效。
  • @ConditionalOnProperty判斷這個(gè)配置是否存在,matchIfMissing = true代表即使配置文件中不存在這個(gè)屬性也是默認(rèn)生效的。

需要注意的是,在spring.factories中的自動(dòng)配置類不是都能生效的,都有各自的生效條件。根據(jù)當(dāng)前不同條件判斷,來決定這個(gè)配置類是否生效;一旦配置類生效,這個(gè)配置類就會(huì)給容器添加各種組件,這些組件的屬性從對(duì)應(yīng)的properties中獲取,這些類里面的每一個(gè)屬性又是和配置文件綁定的。

五、總結(jié)

Spring Boot 的自動(dòng)化配置和啟動(dòng)流程為開發(fā)者提供了簡化的開發(fā)體驗(yàn),降低了配置和啟動(dòng)的復(fù)雜性。通過深入理解 SpringApplication 的啟動(dòng)步驟、自動(dòng)配置的原理以及 Spring Boot 如何無縫集成各類組件,我們不僅能更好地運(yùn)用 Spring Boot,還能更靈活地定制和優(yōu)化應(yīng)用。自動(dòng)配置是 Spring Boot 的核心之一,它通過精巧的條件判斷實(shí)現(xiàn)了“按需配置”,確保應(yīng)用在減少冗余的前提下獲得所需的功能支持。

在實(shí)際開發(fā)中,理解這些原理可以幫助我們?cè)谟龅絾栴}時(shí)更快找到原因,尤其是當(dāng)我們需要進(jìn)行調(diào)優(yōu)或者定制配置時(shí),更能從容應(yīng)對(duì)。希望通過本文的分析,你能夠更全面地掌握 Spring Boot 的核心機(jī)制,并在實(shí)際項(xiàng)目中充分發(fā)揮它的優(yōu)勢。

到此這篇關(guān)于Spring Boot啟動(dòng)加載流程自動(dòng)配置的底層原理的文章就介紹到這了,更多相關(guān)Spring Boot啟動(dòng)過程與自動(dòng)配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何獲取包下所有類中的注解的值(java工具類)

    如何獲取包下所有類中的注解的值(java工具類)

    這篇文章主要介紹了如何獲取包下所有類中的注解的值 (java工具類),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解

    Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解

    這篇文章主要介紹了Springboot實(shí)現(xiàn)WebMvcConfigurer接口定制mvc配置詳解,spring?boot拋棄了傳統(tǒng)xml配置文件,通過配置類(標(biāo)注@Configuration的類,@Configuration配置類相當(dāng)于一個(gè)xml配置文件)以JavaBean形式進(jìn)行相關(guān)配置,需要的朋友可以參考下
    2023-09-09
  • SpringMVC中@RequestMapping注解用法實(shí)例

    SpringMVC中@RequestMapping注解用法實(shí)例

    通過@RequestMapping注解可以定義不同的處理器映射規(guī)則,下面這篇文章主要給大家介紹了關(guān)于SpringMVC中@RequestMapping注解用法的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Java WebSocket客戶端接收大量數(shù)據(jù)的三種方案

    Java WebSocket客戶端接收大量數(shù)據(jù)的三種方案

    WebSocket是一種基于TCP協(xié)議的全雙工通信協(xié)議,它能夠在客戶端和服務(wù)器之間建立一個(gè)持久連接,實(shí)現(xiàn)實(shí)時(shí)的雙向數(shù)據(jù)傳輸,在實(shí)際應(yīng)用中,有時(shí)候我們需要處理大量的數(shù)據(jù),所以本文將介紹如何使用 Java WebSocket 客戶端接收大量數(shù)據(jù),并提供一些優(yōu)化方案
    2023-11-11
  • Java如何輸出windows中的全部漢字

    Java如何輸出windows中的全部漢字

    這篇文章主要介紹了Java如何輸出windows中的全部漢字問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • springboot數(shù)據(jù)庫操作圖文教程

    springboot數(shù)據(jù)庫操作圖文教程

    本文以圖文并茂的形式給大家介紹了springboot數(shù)據(jù)庫操作,感興趣的朋友一起看看吧
    2017-07-07
  • SpringBoot @CompentScan excludeFilters配置無效的解決方案

    SpringBoot @CompentScan excludeFilters配置無效的解決方案

    這篇文章主要介紹了SpringBoot @CompentScan excludeFilters配置無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 深入淺析Java注解框架

    深入淺析Java注解框架

    這篇文章主要介紹了深入淺析Java注解框架的相關(guān)資料,介紹的非常詳細(xì),具有參考價(jià)值,需要的朋友參考下吧
    2016-05-05
  • Mybatis Plus 大數(shù)據(jù)游標(biāo)分頁的實(shí)現(xiàn)

    Mybatis Plus 大數(shù)據(jù)游標(biāo)分頁的實(shí)現(xiàn)

    使用MyBatis Plus的游標(biāo)分頁,我們可以輕松應(yīng)對(duì)大數(shù)據(jù)量的場景,本文主要介紹了Mybatis Plus 大數(shù)據(jù)游標(biāo)分頁的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-07-07
  • Java 獲取服務(wù)器環(huán)境的實(shí)例詳解

    Java 獲取服務(wù)器環(huán)境的實(shí)例詳解

    這篇文章主要介紹了Java 獲取服務(wù)器環(huán)境的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例和輸出結(jié)果,希望能幫助大家理解,需要的朋友可以參考下
    2017-07-07

最新評(píng)論

贡山| 泰宁县| 乐陵市| 临沧市| 四子王旗| 榆树市| 封开县| 乐安县| 襄樊市| 万年县| 台州市| 神木县| 忻城县| 白城市| 三河市| 龙陵县| 中卫市| 东阿县| 姚安县| 大洼县| 辰溪县| 崇州市| 泰州市| 郴州市| 长武县| 邯郸市| 乐清市| 华容县| 屏山县| 库尔勒市| 介休市| 丘北县| 资兴市| 宁波市| 商南县| 东乡族自治县| 张家港市| 游戏| 怀仁县| 阿拉善左旗| 阳新县|