亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

解析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

虎林市| 邛崃市| 霸州市| 聊城市| 神农架林区| 常熟市| 荣昌县| 天津市| 龙口市| 山阳县| 花莲市| 德安县| 衡阳市| 越西县| 巩留县| 门头沟区| 德化县| 揭阳市| 钟山县| 岫岩| 遂溪县| 黔西县| 独山县| 沙洋县| 淮南市| 建昌县| 蓝田县| 田阳县| 阳曲县| 仁布县| 如皋市| 玉门市| 霍山县| 裕民县| 和林格尔县| 天祝| 屯昌县| 永春县| 永善县| 栾城县| 潜山县|