摘要:常規的配置讓開發人員將更多的經歷耗費在了配置文件上。其中有三個注解,,。以前我們需要配置的東西,幫我們自動配置,告訴開啟自動配置功能,這樣自動配置才能生效。
為什么需要自動化配置
??在常規的spring應用程序中,充斥著大量的配置文件,我們需要手動去配置這些文件,如配置組件掃描、視圖解析器、http編碼等等。常規的配置讓開發人員將更多的經歷耗費在了配置文件上。而這些配置都是一些固定模式的配置方式,甚至很多都是模板代碼。那既然是這樣一種情況,有沒有一種可能性,讓spring自動完成這些模板配置工作呢?答案是肯定的,這就是SpringBoot AutoConfiguration產生的初衷。將開發人員從繁重的配置工作中解放出來,把這些繁瑣的配置交由SpringBoot完成,如果我們需要自己配置參數,只需要覆蓋自動配置的參數即可。
SpringBoot自動化配置的核心原理??在之前的文章中,我們看過了SpringBoot的核心注解@SpringBootApplication注解的源碼。其中有三個注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。以前我們需要配置的東西,SpringBoot幫我們自動配置,@EnableAutoConfiguration告訴SpringBoot開啟自動配置功能,這樣自動配置才能生效。
??下面我們來分析一下@EnableAutoConfiguration這個注解的加載過程
Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
??由源碼可知,@EnableAutoConfiguration是一個組合注解,由@AutoConfigurationPackage,@Import(AutoConfigurationImportSelector.class)這兩個注解組成。
@AutoConfigurationPackage??@AutoConfigurationPackage的主要作用是自動配置包
/** * Indicates that the package containing the annotated class should be registered with * {@link AutoConfigurationPackages}. * * @author Phillip Webb * @since 1.3.0 * @see AutoConfigurationPackages */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }@Import(AutoConfigurationImportSelector.class)
??Spring底層注解@Import,給容器中導入一個組件;導入的組件由AutoConfigurationPackages.Registrar.class
將主配置類(@SpringBootApplication標注的類)的所在包以及下面所有子包里面的所有組件掃描到Spring容器。
??AutoConfigurationImportSelector的作用是導入哪些組件的選擇器。將所有需要導入的組件以全類名的方式返回,這些組件就會被添加到容器中;也會給容器導入非常多的自動配置類(xxxAutoConfiguration),就是給容器中導入這個場景需要的所有組件,并配置好這些組件。
??有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作
具體的工作流程如下:
??自動配置主要由AutoConfigurationImportSelector實現的,我們主要從這個類開始講起。AutoConfigurationImportSelector是@EnableAutoConfiguration“@Import”的DeferredImportSelector實現類,由于DeferredImportSelector作為ImportSelector的子接口,所以組件自動配置邏輯均在selectImports(AnnotationMetadata)方法中實現
??自動配置加載過程主要分為以下幾個步驟:
1.判斷是否開啟自動配置
2.從META-INF/spring-autoconfigure-metadata.properties文件中載入屬性配置
3.獲取所有的配置列表
public String[] selectImports(AnnotationMetadata annotationMetadata) { //1.是否開啟自動配置,默認開啟 if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //2.從META-INF/spring-autoconfigure-metadata.properties文件中載入屬性配置(有一些有默認值),獲取注解信息 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //3.獲取所有的配置列表 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }1.是否開啟自動配置,默認開啟
protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; }2.從META-INF/spring-autoconfigure-metadata.properties文件中載入屬性配置
//文件為需要加載的配置類的類路徑 protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { //1.讀取spring-boot-autoconfigure-2.1.6.RELEASE.jar包中pring-autoconfigure-metadata.properties的信息生成urls枚舉對象 Enumeration3.獲取所有的配置列表urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties = new Properties(); //2.解析urls枚舉對象中的信息封裝成properties對象并加載 while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); } //3.根據封裝好的properties對象生成AutoConfigurationMetadata對象返回 return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); } }
/** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param autoConfigurationMetadata the auto-configuration metadata * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } //1.將注解元信息封裝成注解屬性對象 AnnotationAttributes attributes = getAttributes(annotationMetadata); //2.獲取到配置類的全路徑字符串集合 List3.1將注解元信息封裝成注解屬性對象configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); //需要排除的自動裝配類(springboot的主類上 @SpringBootApplication(exclude = {com.demo.XXX.class})指定的排除的自動裝配類) Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); //將需要排除的類從 configurations remove掉 configurations.removeAll(exclusions); //過濾掉不需要裝配的類。過濾的邏輯有很多,比如我們常用的@ConditionXXX注解 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
/** * Return the appropriate {@link AnnotationAttributes} from the * {@link AnnotationMetadata}. By default this method will return attributes for * {@link #getAnnotationClass()}. * @param metadata the annotation metadata * @return annotation attributes */ protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { String name = getAnnotationClass().getName(); AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?"); return attributes; }3.2獲取到配置類的全路徑字符串集合
??getCandidateConfigurations(annotationMetadata, attributes);這個方法中有一個重要方法loadFactoryNames,這個方法是讓SpringFactoryLoader去加載一些組件的名字
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected ListgetCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { /** * 這個方法需要傳入兩個參數getSpringFactoriesLoaderFactoryClass()和getBeanClassLoader() * getSpringFactoriesLoaderFactoryClass()這個方法返回的是EnableAutoConfiguration.class * getBeanClassLoader()這個方法返回的是beanClassLoader(類加載器) */ List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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; } /** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }
??下面來看下loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());這個方法
public static List總結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 (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map > loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = (MultiValueMap)cache.get(classLoader); if(result != null) { return result; } else { try { //如果類加載器不為null,則加載類路徑下spring.factories文件,將其中設置的配置類的全路徑信息封裝 為Enumeration類對象 Enumeration urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //循環Enumeration類對象,根據相應的節點信息生成Properties對象,通過傳入的鍵獲取值,在將值切割為一個個小的字符串轉化為Array,方法result集合中 while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); //讀取文件內容,properties類似于HashMap,包含了屬性的key和value Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); //屬性文件中可以用","分割多個value while(var6.hasNext()) { Entry, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
??springboot底層實現自動配置的步驟:
1.springboot應用啟動
2.@SpringBootApplication起作用
3.@EnableAutoConfiguration
4.@AutoConfigurationPackage:這個組合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通過將Registrar類導入到容器中,而Registrar類作用是掃描主配置類同級目錄以及子包,并將相應的組件導入到springboot創建管理的容器中
5.@Import(AutoConfigurationImportSelector.class):它通過將AutoConfigurationImportSelector類導入到容器中,AutoConfigurationImportSelector類作用是通過selectImports方法實現將配置類信息交給SpringFactory加載器進行一系列的容器創建過程,具體實現可查看上面的源碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75985.html
摘要:常規的配置讓開發人員將更多的經歷耗費在了配置文件上。其中有三個注解,,。以前我們需要配置的東西,幫我們自動配置,告訴開啟自動配置功能,這樣自動配置才能生效。 為什么需要自動化配置 ??在常規的spring應用程序中,充斥著大量的配置文件,我們需要手動去配置這些文件,如配置組件掃描、視圖解析器、http編碼等等。常規的配置讓開發人員將更多的經歷耗費在了配置文件上。而這些配置都是一些固定模...
摘要:用于主類上最最最核心的注解,表示這是一個項目,用于開啟的各項能力。下面我們來分析一下這個注解的組成以及作用通過上面的代碼我們可以看出來是一個組合注解,主要由和這三個注解組成的。通過源碼可以看出也是一個組合注解。 ??SpringBoot項目一般都會有Application的入口類,入口類中會有main方法,這是一個標準的java應用程序的入口方法。@SpringBootApplicat...
摘要:一源碼分析簡述聲明本人使用的開發工具為了解查看源碼,眼睛掃到,這很關鍵。注版本是,該類繼承了。這說明了依賴于配置文件,文件中鍵為對應的配置項注全類名加載到容器。如果用方法獲得,會報錯,所以呀用的方法。 一、源碼分析簡述聲明:本人使用的開發工具為IDEA1、@EnableAutoConfiguration了解 查看源碼,眼睛掃到@Import(AutoConfigurationImpo...
摘要:前言用過的肯定很熟悉,它其中有個重要的特性,就是自動配置平時習慣的一些設置的配置作為默認配置。提倡無配置文件的理念,使用生成的應用完全不會生成任何配置代碼與配置文件。 前言 用過springboot的肯定很熟悉,它其中有個重要的特性,就是自動配置(平時習慣的一些設置的配置作為默認配置)。springboot提倡無XML配置文件的理念,使用springboot生成的應用完全不會生成任何配...
閱讀 1405·2021-11-25 09:43
閱讀 2260·2021-09-27 13:36
閱讀 1114·2021-09-04 16:40
閱讀 1957·2019-08-30 11:12
閱讀 3308·2019-08-29 14:14
閱讀 565·2019-08-28 17:56
閱讀 1320·2019-08-26 13:50
閱讀 1246·2019-08-26 13:29