中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

解析SpringBoot @EnableAutoConfiguration的使用

發布時間:2020-10-23 23:28:02 來源:腳本之家 閱讀:196 作者:李紅歐巴 欄目:編程語言

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

直到接觸Spring Boot 后,發現其可以自動引入二方包的bean。不過一直沒有看這塊的實現原理。直到最近面試的時候被問到。所以就看了下實現邏輯。

使用姿勢

講原理前先說下使用姿勢。

在project A中定義一個bean。

package com.wangzhi;

import org.springframework.stereotype.Service;

@Service
public class Dog {
}

并在該project的resources/META-INF/下創建一個叫spring.factories的文件,該文件內容如下

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));
  }

}

打印結果:

com.wangzhi.Dog@3148f668

原理解析

總體分為兩個部分:一是收集所有spring.factories中EnableAutoConfiguration相關bean的類,二是將得到的類注冊到spring容器中。

收集bean定義類

在spring容器啟動時,會調用到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中指定的類
  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會調用到方法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的類路徑
 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);
        // 讀取文件內容,properties類似于HashMap,包含了屬性的key和value
  Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  for (Map.Entry<?, ?> entry : properties.entrySet()) {
   String factoryClassName = ((String) entry.getKey()).trim();
          // 屬性文件中可以用','分割多個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);
 }
 }

注冊到容器

在上面的流程中得到了所有在spring.factories中指定的bean的類路徑,在processGroupImports方法中會以處理@import注解一樣的邏輯將其導入進容器。

public void processGroupImports() {
  for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
    // getImports即上面得到的所有類路徑的封裝
    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) {
 ...
  // 遍歷收集到的類路徑
  for (SourceClass candidate : importCandidates) {
    ...
    //如果candidate是ImportSelector或ImportBeanDefinitionRegistrar類型其處理邏輯會不一樣,這里不關注
   // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
   // process it as an @Configuration class
   this.importStack.registerImport(
    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
 // 當作 @Configuration 處理  
    processConfigurationClass(candidate.asConfigClass(configClass));
  ...
}
      
  ...
}

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

End

@EnableAutoConfiguration注解簡化了導入了二方包bean的成本。提供一個二方包給其他應用使用,只需要在二方包里將對外暴露的bean定義在spring.factories中就好了。對于不需要的bean,可以在使用方用@EnableAutoConfiguration的exclude屬性進行排除。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

闸北区| 类乌齐县| 延寿县| 咸丰县| 鹤壁市| 广东省| 秀山| 铁力市| 高要市| 梁平县| 固原市| 五原县| 淅川县| 巩义市| 政和县| 安丘市| 铜川市| 新津县| 蕉岭县| 鹤山市| 高淳县| 介休市| 麻栗坡县| 浠水县| 上饶市| 历史| 黄陵县| 公安县| 景宁| 常德市| 临西县| 西安市| 永清县| 兴文县| 综艺| 弋阳县| 余庆县| 沿河| 七台河市| 平阴县| 惠东县|