Spring中的ImportSelector接口原理解析
ImportSelector接口原理
ImportSelector接口是spring中導入外部配置的核心接口,根據(jù)給定的條件(通常是一個或多個注釋屬性)判定要導入那個配置類,在spring自動化配置和@EnableXXX中都有它的存在;
1.ImportSelector接口源碼解析
/** * Interface to be implemented by types that determine which @{@link Configuration} * class(es) should be imported based on a given selection criteria, usually one or * more annotation attributes. * * <p>An {@link ImportSelector} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, * and their respective methods will be called prior to {@link #selectImports}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li> * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li> * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li> * </ul> * * <p>Alternatively, the class may provide a single constructor with one or more of * the following supported parameter types: * <ul> * <li>{@link org.springframework.core.env.Environment Environment}</li> * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li> * <li>{@link java.lang.ClassLoader ClassLoader}</li> * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li> * </ul> * * <p>{@code ImportSelector} implementations are usually processed in the same way * as regular {@code @Import} annotations, however, it is also possible to defer * selection of imports until all {@code @Configuration} classes have been processed * (see {@link DeferredImportSelector} for details). * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 * @see DeferredImportSelector * @see Import * @see ImportBeanDefinitionRegistrar * @see Configuration */ public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. * @return the class names, or an empty array if none */ String[] selectImports(AnnotationMetadata importingClassMetadata); /** * Return a predicate for excluding classes from the import candidates, to be * transitively applied to all classes found through this selector's imports. * <p>If this predicate returns {@code true} for a given fully-qualified * class name, said class will not be considered as an imported configuration * class, bypassing class file loading as well as metadata introspection. * @return the filter predicate for fully-qualified candidate class names * of transitively imported configuration classes, or {@code null} if none * @since 5.2.4 */ @Nullable default Predicate<String> getExclusionFilter() { return null; } }
接口文檔已經說的很明白,其主要作用是收集需要導入的配置類,如果該接口的實現(xiàn)類同時實現(xiàn)了org.springframework.beans.factory.Aware相關接口,如:EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware等,那么在調用其selectImports方法之前先調用上述接口中的回調方法;如果需要在所有的@Configuration處理完再導入,可以實現(xiàn)DeferredImportSelector接口;
2.DeferredImportSelector接口源碼解析
DeferredImportSelector接口是ImportSelector接口的子接口,該接口會在所有的@Configuration配置類(不包括自動化配置類,即spring.factories文件中的配置類)處理完成后運行;當選擇器和@Conditional條件注解一起使用時是特別有用的,此接口還可以和接口Ordered或者@Ordered一起使用,定義多個選擇器的優(yōu)先級;
/** * A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans * have been processed. This type of selector can be particularly useful when the selected * imports are {@code @Conditional}. * * <p>Implementations can also extend the {@link org.springframework.core.Ordered} * interface or use the {@link org.springframework.core.annotation.Order} annotation to * indicate a precedence against other {@link DeferredImportSelector DeferredImportSelectors}. * * <p>Implementations may also provide an {@link #getImportGroup() import group} which * can provide additional sorting and filtering logic across different selectors. * * @author Phillip Webb * @author Stephane Nicoll * @since 4.0 */ public interface DeferredImportSelector extends ImportSelector { /** * 返回指定的導入結果集 */ @Nullable default Class<? extends Group> getImportGroup() { return null; } /** * 用于從不同DeferredImportSelector中獲取需要導入類的結果集 */ interface Group { /** * 根據(jù)AnnotationMetadata注解元數(shù)據(jù)獲取@Configuration配置的@Import注解導入的DeferredImportSelector選擇器對應的bean */ void process(AnnotationMetadata metadata, DeferredImportSelector selector); /** * 返回類應該導入的Entry */ Iterable<Entry> selectImports(); /** * 存放要導入類的全限定名及AnnotationMetadata注解元數(shù)據(jù) */ class Entry { private final AnnotationMetadata metadata; private final String importClassName; public Entry(AnnotationMetadata metadata, String importClassName) { this.metadata = metadata; this.importClassName = importClassName; } /** * 返回要引入的Configuration類的AnnotationMetadata注解元數(shù)據(jù) */ public AnnotationMetadata getMetadata() { return this.metadata; } /** * 返回要導入類的全限定名 */ public String getImportClassName() { return this.importClassName; } @Override public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } Entry entry = (Entry) other; return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName)); } @Override public int hashCode() { return (this.metadata.hashCode() * 31 + this.importClassName.hashCode()); } @Override public String toString() { return this.importClassName; } } } }
3.示例
AutoConfigurationImportSelector是DeferredImportSelector接口的實現(xiàn)類,用于處理EnableAutoConfiguration自動化配置,
我們知道SpringFactoriesLoader類是自動化配置的核心類,用來將spring.factories配置文件中定義的類加載到內存之中,供后面的程序將其注冊到IOC容器之中;AutoConfigurationImportSelector類是DeferredImportSelector接口的一個子類,它的作用就是將SpringFactoriesLoader類加載到內中的配置類獲取到,交給后置處理器加載到內存中(不是本文重點);
AutoConfigurationGroup是一個靜態(tài)內部類,實現(xiàn)了DeferredImportSelector.Group接口,所以其作用是根據(jù)注解的AnnotationMetadata元數(shù)據(jù)獲取導入的DeferredImportSelector接口實現(xiàn)類對應的自動化配置類;
- AutoConfigurationImportSelector.AutoConfigurationGroup類的核心方法process用于獲取自動化配置類:
//存放配置類全限定名和注解元數(shù)據(jù)類 private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>(); //存放配置類的實體對象(包含需要導入的配置類、排除的配置類) private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>(); @Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { //獲取spring.factories配置文件中的配置類(包括需要導入的、不需要導入的) AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }
- AutoConfigurationEntry類用于存放排除掉的配置類,以及需要導入的配置類:
protected static class AutoConfigurationEntry { //需要導入的配置類 private final List<String> configurations; //排除不用導入的配置類 private final Set<String> exclusions; }
- AutoConfigurationImportSelector#getAutoConfigurationEntry方法獲取基于配置類注解的AnnotationMetaData元數(shù)據(jù)導入Configuration配置類
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } //獲取注解的屬性配置(exclude和excludeName) AnnotationAttributes attributes = getAttributes(annotationMetadata); //獲取自動化配置文件spirng.factories中的配置類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //刪除List中重復的配置類(去重方法值得參考) configurations = removeDuplicates(configurations); //獲取排除導入的配置類(包括spring.autoconfigure.exclude屬性配置及注解屬性exclude和excludeName) Set<String> exclusions = getExclusions(annotationMetadata, attributes); //檢驗排除類 checkExcludedClasses(configurations, exclusions); //刪除掉排除的類 configurations.removeAll(exclusions); //獲取過濾器,并對配置類進行過濾 configurations = getConfigurationClassFilter().filter(configurations); //觸發(fā)自動化配置導入事件 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
- AutoConfigurationImportSelector#getCandidateConfigurations方法用于獲取spring.factories配置文件中的配置類(其實際獲取是直接從SpringFactoriesLoader類中的cache獲取的,已經在初始化器階段加載到緩存中了):
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //獲取自動化配置對應spring.factories文件中的配置類, List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; }
返回加載配置類:
protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
- AutoConfigurationImportSelector#removeDuplicates方法刪除重復的配置類
protected final <T> List<T> removeDuplicates(List<T> list) { return new ArrayList<>(new LinkedHashSet<>(list)); }
很好的去重思路,以后可以參考使用;
- AutoConfigurationImportSelector#getExclusions獲取排除導入的配置類
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashSet<>(); //獲取exclude屬性指定的配置類 excluded.addAll(asList(attributes, "exclude")); //獲取excludeName屬性指定的配置類 excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); //獲取spring.autoconfigure.exclude屬性指定的配置類 excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }
- AutoConfigurationImportSelector#getExcludeAutoConfigurationsProperty獲取spring.autoconfigure.exclude屬性配置類
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; protected List<String> getExcludeAutoConfigurationsProperty() { Environment environment = getEnvironment(); if (environment == null) { return Collections.emptyList(); } if (environment instanceof ConfigurableEnvironment) { Binder binder = Binder.get(environment); //獲取配置文件中排除導入配置類 return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) .orElse(Collections.emptyList()); } String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList(); }
- AutoConfigurationImportSelector#getConfigurationClassFilter
private ConfigurationClassFilter getConfigurationClassFilter() { if (this.configurationClassFilter == null) { List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) { invokeAwareMethods(filter); } this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters); } return this.configurationClassFilter; }
- AutoConfigurationImportSelector#fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) { List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } }
- AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports獲取上述process方法處理后的配置類
@Override public Iterable<Entry> selectImports() { if (this.autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } //獲取所有需要排除的配置類 Set<String> allExclusions = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); //獲取所有經過自動化配置過濾器的配置類 Set<String> processedConfigurations = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); //排除過濾后配置類中需要排除的類 processedConfigurations.removeAll(allExclusions); return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
到此這篇關于Spring中的ImportSelector接口原理解析的文章就介紹到這了,更多相關ImportSelector接口原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot整合Elasticsearch并實現(xiàn)CRUD操作
這篇文章主要介紹了SpringBoot整合Elasticsearch并實現(xiàn)CRUD操作,需要的朋友可以參考下2018-03-03Spring Cloud OAuth2中/oauth/token的返回內容格式
Spring Cloud OAuth2 生成access token的請求/oauth/token的返回內容就需要自定義,本文就詳細介紹一下,感興趣的可以了解一下2021-07-07Java中LambdaQueryWrapper設置自定義排序代碼示例
這篇文章主要給大家介紹了關于Java中LambdaQueryWrapper設置自定義排序的相關資料,lambdaquerywrapper是MyBatis-Plus框架中的一個查詢條件構造器,它可以用于構建自定義的查詢條件,需要的朋友可以參考下2023-12-12mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程
這篇文章主要為大家詳細介紹了mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程,感興趣的小伙伴們可以參考一下2016-06-06