欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot自動配置原理詳解

 更新時間:2021年12月28日 10:48:06   作者:小伙子vae  
SpringBoot的誕生就是為了簡化Spring中繁瑣的XML配置,其本質(zhì)依然還是Spring框架,使用SpringBoot之后可以不使用任何XML配置來啟動一個服務(wù),使得我們在使用微服務(wù)架構(gòu)時可以更加快速的建立一個應(yīng)用。本文將為具體介紹一下SpringBoot的原理,需要的可以參考一下

閱讀收獲

理解SpringBoot自動配置原理

一、SpringBoot是什么

SpringBoot 的誕生就是為了簡化 Spring 中繁瑣的 XML 配置,其本質(zhì)依然還是Spring框架,使用SpringBoot之后可以不使用任何 XML 配置來啟動一個服務(wù),使得我們在使用微服務(wù)架構(gòu)時可以更加快速的建立一個應(yīng)用。

簡單來說就是SpringBoot其實不是什么新的框架,它默認(rèn)配置了很多框架的使用方式。

二、SpringBoot的特點

  • 提供了固定的配置來簡化配置,即約定大約配置
  • 盡可能地自動配置 Spring 和第三方庫,即能自動裝配
  • 內(nèi)嵌容器,創(chuàng)建獨立的 Spring 應(yīng)用
  • 讓測試變的簡單,內(nèi)置了JUnit、Spring Boot Test等多種測試框架,方便測試
  • 提供可用于生產(chǎn)的特性,如度量、運(yùn)行狀況檢查和外部化配置。
  • 完全不需要生成代碼,也不需要 XML 配置。

三、啟動類

下面探究SpringBoot的啟動原理,關(guān)于一些細(xì)節(jié)就不贅述,我們捉住主線分析即可。

注意:本文的 SpringBoot 版本為 2.6.1

3.1 @SpringBootApplication

一切的來自起源SpringBoot的啟動類,我們發(fā)現(xiàn)main方法上面有個注解:@SpringBootApplication

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

@SpringBootApplication 標(biāo)注在某個類上說明這個類是 SpringBoot 的主配置類, SpringBoot 就應(yīng)該運(yùn)行這個類的main方法來啟動 SpringBoot 應(yīng)用;它的本質(zhì)是一個組合注解,我們點進(jìn)去查看該類的元信息主要包含3個注解:

@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 {
  • @SpringBootConfiguration(里面就是@Configuration,標(biāo)注當(dāng)前類為配置類,其實只是做了一層封裝改了個名字而已)
  • @EnableAutoConfiguration(開啟自動配置)
  • @ComponentScan(包掃描)

注:@Inherited是一個標(biāo)識,用來修飾注解,如果一個類用上了@Inherited修飾的注解,那么其子類也會繼承這個注解

我們下面逐一分析這3個注解作用

3.1.1 @SpringBootConfiguration

我們繼續(xù)點@SpringBootConfiguration進(jìn)去查看源碼如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@Configuration標(biāo)注在某個類上,表示這是一個 springboot的配置類。可以向容器中注入組件。

3.1.2 @ComponentScan

  • @ComponentScan:配置用于 Configuration 類的組件掃描指令。
  • 提供與 Spring XML 的 <context:component-scan> 元素并行的支持。
  • 可以 basePackageClasses 或basePackages 來定義要掃描的特定包。 如果沒有定義特定的包,將從聲明該注解的類的包開始掃描。

3.1.3 @EnableAutoConfiguration

  • @EnableAutoConfiguration顧名思義就是:開啟自動導(dǎo)入配置
  • 這個注解是SpringBoot的重點,我們下面詳細(xì)講解

四、@EnableAutoConfiguration

我們點進(jìn)去看看該注解有什么內(nèi)容

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage   //自動導(dǎo)包
@Import({AutoConfigurationImportSelector.class}) //自動配置導(dǎo)入選擇
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

4.1 @AutoConfigurationPackage

自動導(dǎo)入配置包

點進(jìn)去查看代碼:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

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

@Import 為spring的注解,導(dǎo)入一個配置文件,在springboot中為給容器導(dǎo)入一個組件,而導(dǎo)入的組件由 AutoConfigurationPackages.class的內(nèi)部類Registrar.class 執(zhí)行邏輯來決定是如何導(dǎo)入的。

4.1.1 @Import({Registrar.class})

點Registrar.class進(jìn)去查看源碼如下:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //斷點
        AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
    }

    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
    }
}

注:Registrar實現(xiàn)了ImportBeanDefinitionRegistrar類,就可以被注解@Import導(dǎo)入到spring容器里。

這個地方打斷點

運(yùn)行可以查看到(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])的值為com.ljw.springbootwork:當(dāng)前啟動類所在的包名

結(jié)論:@AutoConfigurationPackage 就是將主配置類(@SpringBootApplication 標(biāo)注的類)所在的包下面所有的組件都掃描注冊到 spring 容器中。

4.2? @Import({AutoConfigurationImportSelector.class})

作用:AutoConfigurationImportSelector開啟自動配置類的導(dǎo)包的選擇器,即是帶入哪些類,有選擇性的導(dǎo)入

點AutoConfigurationImportSelector.class進(jìn)入查看源碼,這個類中有兩個方法見名知意:

1.selectImports:選擇需要導(dǎo)入的組件

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

2.getAutoConfigurationEntry:根據(jù)導(dǎo)入的@Configuration類的AnnotationMetadata返回AutoConfigurationImportSelector.AutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
         // 這打個斷點,看看 返回的數(shù)據(jù)
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        //刪除重復(fù)項
        configurations = this.removeDuplicates(configurations);
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        //檢查
        this.checkExcludedClasses(configurations, exclusions);
        //刪除需要排除的依賴
        configurations.removeAll(exclusions);
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

this.getCandidateConfigurations(annotationMetadata, attributes)這里斷點查看

configurations數(shù)組長度為133,并且文件后綴名都為 **AutoConfiguration

結(jié)論: 這些都是候選的配置類,經(jīng)過去重,去除需要的排除的依賴,最終的組件才是這個環(huán)境需要的所有組件。有了自動配置,就不需要我們自己手寫配置的值了,配置類有默認(rèn)值的。

我們繼續(xù)往下看看是如何返回需要配置的組件的

4.2.1 getCandidateConfigurations(annotationMetadata, attributes)

方法如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

這里有句斷言: Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

意思是:“在 META-INF/spring.factories 中沒有找到自動配置類。如果您使用自定義包裝,請確保該文件是正確的。“

結(jié)論: 即是要loadFactoryNames()方法要找到自動的配置類返回才不會報錯。

4.2.1.1 getSpringFactoriesLoaderFactoryClass()

我們點進(jìn)去發(fā)現(xiàn):this.getSpringFactoriesLoaderFactoryClass()返回的是EnableAutoConfiguration.class這個注解。這個注解和@SpringBootApplication下標(biāo)識注解是同一個注解。

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

結(jié)論:獲取一個能加載自動配置類的類,即SpringBoot默認(rèn)自動配置類為EnableAutoConfiguration

4.2.2 SpringFactoriesLoader

SpringFactoriesLoader工廠加載機(jī)制是Spring內(nèi)部提供的一個約定俗成的加載方式,只需要在模塊的META-INF/spring.factories文件,這個Properties格式的文件中的key是接口、注解、或抽象類的全名,value是以逗號 “ , “ 分隔的實現(xiàn)類,使用SpringFactoriesLoader來實現(xiàn)相應(yīng)的實現(xiàn)類注入Spirng容器中。

注:會加載所有jar包下的classpath路徑下的META-INF/spring.factories文件,這樣文件不止一個。

4.2.2.1 loadFactoryNames()

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

斷點查看factoryTypeName:

先是將 EnableAutoConfiguration.class 傳給了 factoryType?

然后String factoryTypeName = factoryType.getName();,所以factoryTypeName 值為? org.springframework.boot.autoconfigure.EnableAutoConfiguration

4.2.2.2 loadSpringFactories()

接著查看loadSpringFactories方法的作用

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    //斷點查看
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {
      //注意這里:META-INF/spring.factories
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
            //斷點
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }

      // Replace all lists with unmodifiable lists containing unique elements
      //去重,斷點查看result值
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}

這里的 FACTORIES_RESOURCE_LOCATION 在上面有定義:META-INF/spring.factories

public final class SpringFactoriesLoader {

   /**
    * The location to look for factories.
    * <p>Can be present in multiple JAR files.
    */
   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

META-INF/spring.factories文件在哪里??

在所有引入的java包的當(dāng)前類路徑下的META-INF/spring.factories文件都會被讀取,如:

斷點查看result值如下:

該方法作用是加載所有依賴的路徑META-INF/spring.factories文件,通過map結(jié)構(gòu)保存,key為文件中定義的一些標(biāo)識工廠類,value就是能自動配置的一些工廠實現(xiàn)的類,value用list保存并去重。

在回看 loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());

因為 loadFactoryNames 方法攜帶過來的第一個參數(shù)為 EnableAutoConfiguration.class,所以 factoryType 值也為 EnableAutoConfiguration.class,那么 factoryTypeName 值為 EnableAutoConfiguration。拿到的值就是META-INF/spring.factories文件下的key為

org.springframework.boot.autoconfigure.EnableAutoConfiguration的值

getOrDefault 當(dāng) Map 集合中有這個 key 時,就使用這個 key值,如果沒有就使用默認(rèn)值空數(shù)組

結(jié)論:

  • loadSpringFactories()該方法就是從“META-INF/spring.factories”中加載給定類型的工廠實現(xiàn)的完全限定類名放到map中
  • loadFactoryNames()是根據(jù)SpringBoot的啟動生命流程,當(dāng)需要加載自動配置類時,就會傳入org.springframework.boot.autoconfigure.EnableAutoConfiguration參數(shù),從map中查找key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,這些值通過反射加到容器中,之后的作用就是用它們來做自動配置,這就是Springboot自動配置開始的地方
  • 只有這些自動配置類進(jìn)入到容器中以后,接下來這個自動配置類才開始進(jìn)行啟動
  • 當(dāng)需要其他的配置時如監(jiān)聽相關(guān)配置:listenter,就傳不同的參數(shù),獲取相關(guān)的listenter配置。

五、流程總結(jié)圖

六、常用的Conditional注解

在加載自動配置類的時候,并不是將spring.factories的配置全部加載進(jìn)來,而是通過@Conditional等注解的判斷進(jìn)行動態(tài)加載

@Conditional其實是spring底層注解,意思就是根據(jù)不同的條件,來進(jìn)行自己不同的條件判斷,如果滿足指定的條件,那么配置類里邊的配置才會生效。

常用的Conditional注解:

  • @ConditionalOnClass : classpath中存在該類時起效
  • @ConditionalOnMissingClass : classpath中不存在該類時起效
  • @ConditionalOnBean : DI容器中存在該類型Bean時起效
  • @ConditionalOnMissingBean : DI容器中不存在該類型Bean時起效
  • @ConditionalOnSingleCandidate : DI容器中該類型Bean只有一個或@Primary的只有一個時起效
  • @ConditionalOnExpression : SpEL表達(dá)式結(jié)果為true時
  • @ConditionalOnProperty : 參數(shù)設(shè)置或者值一致時起效
  • @ConditionalOnResource : 指定的文件存在時起效
  • @ConditionalOnJndi : 指定的JNDI存在時起效
  • @ConditionalOnJava : 指定的Java版本存在時起效
  • @ConditionalOnWebApplication : Web應(yīng)用環(huán)境下起效
  • @ConditionalOnNotWebApplication : 非Web應(yīng)用環(huán)境下起效

七、@Import支持導(dǎo)入的三種方式

1.帶有@Configuration的配置類

2.ImportSelector 的實現(xiàn)

3.ImportBeanDefinitionRegistrar 的實現(xiàn)

以上就是SpringBoot自動配置原理詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot自動配置原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 圖文詳解Java環(huán)境變量配置方法

    圖文詳解Java環(huán)境變量配置方法

    這篇文章主要以圖文結(jié)合的方式詳細(xì)介紹了Java環(huán)境變量配置方法,文中步驟介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • Java中Jackson的多態(tài)反序列化詳解

    Java中Jackson的多態(tài)反序列化詳解

    這篇文章主要介紹了Java中Jackson的多態(tài)反序列化詳解,多態(tài)序列化與反序列化,主要是借助于Jackson的@JsonTypeInfo與@JsonSubTypes注解實現(xiàn),下面將通過幾個例子來簡述其運(yùn)用,需要的朋友可以參考下
    2023-11-11
  • Java web.xml之contextConfigLocation作用案例詳解

    Java web.xml之contextConfigLocation作用案例詳解

    這篇文章主要介紹了Java web.xml之contextConfigLocation作用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • SpringMVC的處理器適配器-HandlerAdapter的用法及說明

    SpringMVC的處理器適配器-HandlerAdapter的用法及說明

    這篇文章主要介紹了SpringMVC的處理器適配器-HandlerAdapter的用法及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java之StringBuffer常見使用方法解析

    java之StringBuffer常見使用方法解析

    這篇文章主要介紹了java之StringBuffer常見使用方法解析,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • Mybatis動態(tài)sql超詳細(xì)講解

    Mybatis動態(tài)sql超詳細(xì)講解

    動態(tài)SQL是MyBatis的強(qiáng)大特性之一,顧名思義就是會動的SQL,即是能夠靈活的根據(jù)某種條件拼接出完整的SQL語句,下面這篇文章主要給大家介紹了關(guān)于Mybatis動態(tài)sql的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • Spring AOP 切面@Around注解的用法說明

    Spring AOP 切面@Around注解的用法說明

    這篇文章主要介紹了Spring AOP 切面@Around注解的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 關(guān)于MyBatis 查詢數(shù)據(jù)時屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù))

    關(guān)于MyBatis 查詢數(shù)據(jù)時屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù))

    這篇文章主要介紹了MyBatis 查詢數(shù)據(jù)時屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù)),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Java實現(xiàn)文件的分割與合并

    Java實現(xiàn)文件的分割與合并

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)文件的分割與合并,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹

    Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹

    這篇文章主要介紹了Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹,在Java中存在兩種綁定方式,一種為靜態(tài)綁定,又稱作早期綁定,另一種就是動態(tài)綁定,亦稱為后期綁定,需要的朋友可以參考下
    2015-01-01

最新評論