您好,登錄后才能下訂單哦!
本篇內容主要講解“SpringBoot@EnableAutoConfiguration的使用方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“SpringBoot@EnableAutoConfiguration的使用方法”吧!
使用姿勢
講原理前先說下使用姿勢。
在project A中定義一個bean。
package com.wangzhi;import org.springframework.stereotype.Service;@Servicepublic 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;@EnableAutoConfigurationpublic 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屬性進行排除。
到此,相信大家對“SpringBoot@EnableAutoConfiguration的使用方法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。