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

解析SpringBoot @EnableAutoConfiguration的使用

 更新時(shí)間:2019年09月10日 11:02:11   作者:李紅歐巴  
這篇文章主要介紹了解析SpringBoot @EnableAutoConfiguration的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

剛做后端開(kāi)發(fā)的時(shí)候,最早接觸的是基礎(chǔ)的spring,為了引用二方包提供bean,還需要在xml中增加對(duì)應(yīng)的包<context:component-scan base-package="xxx" /> 或者增加注解@ComponentScan({ "xxx"})。當(dāng)時(shí)覺(jué)得挺urgly的,但也沒(méi)有去研究有沒(méi)有更好的方式。

直到接觸Spring Boot 后,發(fā)現(xiàn)其可以自動(dòng)引入二方包的bean。不過(guò)一直沒(méi)有看這塊的實(shí)現(xiàn)原理。直到最近面試的時(shí)候被問(wèn)到。所以就看了下實(shí)現(xiàn)邏輯。

使用姿勢(shì)

講原理前先說(shuō)下使用姿勢(shì)。

在project A中定義一個(gè)bean。

package com.wangzhi;

import org.springframework.stereotype.Service;

@Service
public class Dog {
}

并在該project的resources/META-INF/下創(chuàng)建一個(gè)叫spring.factories的文件,該文件內(nèi)容如下

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wangzhi.Dog

然后在project B中引用project A的jar包。

projectA代碼如下:

package com.wangzhi.springbootdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
public class SpringBootDemoApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class, args);
    System.out.println(context.getBean(com.wangzhi.Dog.class));
  }

}

打印結(jié)果:

com.wangzhi.Dog@3148f668

原理解析

總體分為兩個(gè)部分:一是收集所有spring.factories中EnableAutoConfiguration相關(guān)bean的類(lèi),二是將得到的類(lèi)注冊(cè)到spring容器中。

收集bean定義類(lèi)

在spring容器啟動(dòng)時(shí),會(huì)調(diào)用到AutoConfigurationImportSelector#getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(
    AutoConfigurationMetadata autoConfigurationMetadata,
    AnnotationMetadata annotationMetadata) {
  if (!isEnabled(annotationMetadata)) {
    return EMPTY_ENTRY;
  }
  // EnableAutoConfiguration注解的屬性:exclude,excludeName等
  AnnotationAttributes attributes = getAttributes(annotationMetadata);
  // 得到所有的Configurations
  List<String> configurations = getCandidateConfigurations(annotationMetadata,
      attributes);
  // 去重
  configurations = removeDuplicates(configurations);
  // 刪除掉exclude中指定的類(lèi)
  Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);
  configurations = filter(configurations, autoConfigurationMetadata);
  fireAutoConfigurationImportEvents(configurations, exclusions);
  return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations會(huì)調(diào)用到方法loadFactoryNames:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    // factoryClassName為org.springframework.boot.autoconfigure.EnableAutoConfiguration
 String factoryClassName = factoryClass.getName();
    // 該方法返回的是所有spring.factories文件中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的類(lèi)路徑
 return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
 }


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
 MultiValueMap<String, String> result = cache.get(classLoader);
 if (result != null) {
  return result;
 }

 try {
      // 找到所有的"META-INF/spring.factories"
  Enumeration<URL> urls = (classLoader != null ?
   classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
   ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  result = new LinkedMultiValueMap<>();
  while (urls.hasMoreElements()) {
  URL url = urls.nextElement();
  UrlResource resource = new UrlResource(url);
        // 讀取文件內(nèi)容,properties類(lèi)似于HashMap,包含了屬性的key和value
  Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  for (Map.Entry<?, ?> entry : properties.entrySet()) {
   String factoryClassName = ((String) entry.getKey()).trim();
          // 屬性文件中可以用','分割多個(gè)value
   for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
   result.add(factoryClassName, factoryName.trim());
   }
  }
  }
  cache.put(classLoader, result);
  return result;
 }
 catch (IOException ex) {
  throw new IllegalArgumentException("Unable to load factories from location [" +
   FACTORIES_RESOURCE_LOCATION + "]", ex);
 }
 }

注冊(cè)到容器

在上面的流程中得到了所有在spring.factories中指定的bean的類(lèi)路徑,在processGroupImports方法中會(huì)以處理@import注解一樣的邏輯將其導(dǎo)入進(jìn)容器。

public void processGroupImports() {
  for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
    // getImports即上面得到的所有類(lèi)路徑的封裝
    grouping.getImports().forEach(entry -> {
      ConfigurationClass configurationClass = this.configurationClasses.get(
          entry.getMetadata());
      try {
        // 和處理@Import注解一樣
        processImports(configurationClass, asSourceClass(configurationClass),
            asSourceClasses(entry.getImportClassName()), false);
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to process import candidates for configuration class [" +
                configurationClass.getMetadata().getClassName() + "]", ex);
      }
    });
  }
}

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
  Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
 ...
  // 遍歷收集到的類(lèi)路徑
  for (SourceClass candidate : importCandidates) {
    ...
    //如果candidate是ImportSelector或ImportBeanDefinitionRegistrar類(lèi)型其處理邏輯會(huì)不一樣,這里不關(guān)注
   // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
   // process it as an @Configuration class
   this.importStack.registerImport(
    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
 // 當(dāng)作 @Configuration 處理  
    processConfigurationClass(candidate.asConfigClass(configClass));
  ...
}
      
  ...
}

可以看到,在第一步收集的bean類(lèi)定義,最終會(huì)被以Configuration一樣的處理方式注冊(cè)到容器中。

End

@EnableAutoConfiguration注解簡(jiǎn)化了導(dǎo)入了二方包bean的成本。提供一個(gè)二方包給其他應(yīng)用使用,只需要在二方包里將對(duì)外暴露的bean定義在spring.factories中就好了。對(duì)于不需要的bean,可以在使用方用@EnableAutoConfiguration的exclude屬性進(jìn)行排除。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 最新Java?泛型中的通配符講解

    最新Java?泛型中的通配符講解

    Java的泛型是偽泛型,那是因?yàn)榉盒托畔⒅淮嬖谟诖a編譯階段,在生成的字節(jié)碼中是不包含泛型中的類(lèi)型信息的,使用泛型的時(shí)候加上類(lèi)型參數(shù),在編譯器編譯的時(shí)候會(huì)去掉,這個(gè)過(guò)程為類(lèi)型擦除,這篇文章主要介紹了Java?泛型中的通配符,需要的朋友可以參考下
    2022-06-06
  • SpringBoot項(xiàng)目啟動(dòng)時(shí)增加自定義Banner的簡(jiǎn)單方法

    SpringBoot項(xiàng)目啟動(dòng)時(shí)增加自定義Banner的簡(jiǎn)單方法

    最近看到springboot可以自定義啟動(dòng)時(shí)的banner,然后自己試了一下,下面這篇文章主要給大家介紹了SpringBoot項(xiàng)目啟動(dòng)時(shí)增加自定義Banner的簡(jiǎn)單方法,需要的朋友可以參考下
    2022-01-01
  • Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟

    Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟

    這篇文章主要介紹了Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • RestClient?通過(guò)攔截器實(shí)現(xiàn)請(qǐng)求加密的示例

    RestClient?通過(guò)攔截器實(shí)現(xiàn)請(qǐng)求加密的示例

    本文介紹了如何通過(guò)攔截器實(shí)現(xiàn)請(qǐng)求加密,并通過(guò)RestClient優(yōu)化了加密過(guò)程,傳統(tǒng)的加密方法依賴(lài)對(duì)象轉(zhuǎn)換和序列化處理,容易導(dǎo)致加密不一致或難以調(diào)試的問(wèn)題,通過(guò)引入攔截器,可以直接操作請(qǐng)求體,避免了不必要的轉(zhuǎn)換步驟,確保加密過(guò)程與請(qǐng)求體完全一致,感興趣的朋友一起看看吧
    2025-02-02
  • 基于Rest的API解決方案(jersey與swagger集成)

    基于Rest的API解決方案(jersey與swagger集成)

    下面小編就為大家?guī)?lái)一篇基于Rest的API解決方案(jersey與swagger集成)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • 解析Java和Eclipse中加載本地庫(kù)(.dll文件)的詳細(xì)說(shuō)明

    解析Java和Eclipse中加載本地庫(kù)(.dll文件)的詳細(xì)說(shuō)明

    本篇文章是對(duì)Java和Eclipse中加載本地庫(kù)(.dll文件)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 使用java實(shí)現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù)

    使用java實(shí)現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了如何使用java實(shí)現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • Springboot 實(shí)現(xiàn)數(shù)據(jù)庫(kù)備份還原的方法

    Springboot 實(shí)現(xiàn)數(shù)據(jù)庫(kù)備份還原的方法

    這篇文章主要介紹了Springboot 實(shí)現(xiàn)數(shù)據(jù)庫(kù)備份還原的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java創(chuàng)建多線(xiàn)程異步執(zhí)行實(shí)現(xiàn)代碼解析

    Java創(chuàng)建多線(xiàn)程異步執(zhí)行實(shí)現(xiàn)代碼解析

    這篇文章主要介紹了Java創(chuàng)建多線(xiàn)程異步執(zhí)行實(shí)現(xiàn)代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot中整合Shiro實(shí)現(xiàn)權(quán)限管理的示例代碼

    SpringBoot中整合Shiro實(shí)現(xiàn)權(quán)限管理的示例代碼

    這篇文章主要介紹了SpringBoot中整合Shiro實(shí)現(xiàn)權(quán)限管理的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評(píng)論

邓州市| 改则县| 重庆市| 会东县| 盐城市| 镇安县| 封丘县| 恩平市| 怀远县| 德清县| 河间市| 大港区| 且末县| 重庆市| 兴山县| 许昌县| 潞西市| 仁化县| 黄平县| 泸水县| 丹寨县| 新泰市| 伊川县| 樟树市| 绩溪县| 阿勒泰市| 南陵县| 西丰县| 营口市| 五峰| 襄汾县| 丹寨县| 岐山县| 竹山县| 巴马| 古蔺县| 邮箱| 长岛县| 东莞市| 宕昌县| 蒲城县|