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;
}
}接口文檔已經(jīng)說的很明白,其主要作用是收集需要導入的配置類,如果該接口的實現(xiàn)類同時實現(xiàn)了org.springframework.beans.factory.Aware相關(guān)接口,如:EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware等,那么在調(diào)用其selectImports方法之前先調(diào)用上述接口中的回調(diào)方法;如果需要在所有的@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 {
/**
* 返回指定的導入結(jié)果集
*/
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
/**
* 用于從不同DeferredImportSelector中獲取需要導入類的結(jié)果集
*/
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配置文件中定義的類加載到內(nèi)存之中,供后面的程序?qū)⑵渥缘絀OC容器之中;AutoConfigurationImportSelector類是DeferredImportSelector接口的一個子類,它的作用就是將SpringFactoriesLoader類加載到內(nèi)中的配置類獲取到,交給后置處理器加載到內(nèi)存中(不是本文重點);
AutoConfigurationGroup是一個靜態(tài)內(nè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獲取的,已經(jīng)在初始化器階段加載到緩存中了):
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());
//獲取所有經(jīng)過自動化配置過濾器的配置類
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());
}到此這篇關(guān)于Spring中的ImportSelector接口原理解析的文章就介紹到這了,更多相關(guān)ImportSelector接口原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Elasticsearch并實現(xiàn)CRUD操作
這篇文章主要介紹了SpringBoot整合Elasticsearch并實現(xiàn)CRUD操作,需要的朋友可以參考下2018-03-03
Spring Cloud OAuth2中/oauth/token的返回內(nèi)容格式
Spring Cloud OAuth2 生成access token的請求/oauth/token的返回內(nèi)容就需要自定義,本文就詳細介紹一下,感興趣的可以了解一下2021-07-07
Java中LambdaQueryWrapper設置自定義排序代碼示例
這篇文章主要給大家介紹了關(guān)于Java中LambdaQueryWrapper設置自定義排序的相關(guān)資料,lambdaquerywrapper是MyBatis-Plus框架中的一個查詢條件構(gòu)造器,它可以用于構(gòu)建自定義的查詢條件,需要的朋友可以參考下2023-12-12
mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程
這篇文章主要為大家詳細介紹了mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程,感興趣的小伙伴們可以參考一下2016-06-06

