javascript
Spring.ImportSelector接口
一、關(guān)于ImportSelector接口
ImportSelector接口是至spring中導(dǎo)入外部配置的核心接口,在SpringBoot的自動化配置和@EnableXXX(功能性注解)都有它的存在
二、ImportSelector接口源碼
package org.springframework.context.annotation;import org.springframework.core.type.AnnotationMetadata;/*** 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>ImportSelectors 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* @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.*/String[] selectImports(AnnotationMetadata importingClassMetadata);}該接口文檔上說的明明白白,其主要作用是收集需要導(dǎo)入的配置類,如果該接口的實現(xiàn)類同時實現(xiàn)EnvironmentAware, BeanFactoryAware ,BeanClassLoaderAware或者ResourceLoaderAware,那么在調(diào)用其selectImports方法之前先調(diào)用上述接口中對應(yīng)的方法,如果需要在所有的@Configuration處理完再導(dǎo)入時可以實現(xiàn)DeferredImportSelector接口。
三、例子
@Import(MyImportSelector.class) public class AppConfig { } public class MyDao {public void query(){System.out.println("query MyDao for MyImportSelect");} } public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{MyDao.class.getName()};} } public class TestImpo {public static void main(String[] args) {AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);MyDao myDao = ctx.getBean(MyDao.class);myDao.query();} } 運行打印 query MyDao for MyImportSelect從上面的例子看,盡管程序上沒有把MyImportSelect類放到Spring容器中,也沒有把MyDao放到Spring容器中,但是在測試上就可以把MyDao從容器中拿出來,并且正常執(zhí)行。不知道大家看到這里有什么感覺,到這里我其實是有疑問的。我這么做有個卵用噻。我是有病吧,直接把類上加個@Component注冊進去不香嗎?所以這個ImportSelector把程序搞這么復(fù)雜是有毛病吧,把簡單的功能搞這么復(fù)雜。
四、ImportSelector真正的作用
其實Spring既然這么設(shè)計,那肯定是有用的。那么有什么用呢?設(shè)想這樣一個場景,如果有些功能我們并不需要Spring在一開始就加載進去,而是需要Spring幫助我們把這些功能動態(tài)加載進去,這時候這個ImportSelector的作用就來了。我們完全可以把實現(xiàn)這個接口的類做成一個開關(guān),用來開啟或者關(guān)閉某一個或者某些功能類。比如說我們上面的例子MyDao,假設(shè)這個MyDao實現(xiàn)的功能是一個擴展功能,在正式的生產(chǎn)上不一定用的到。如果說一個包下有100個類,那么使用掃描去屏蔽這個類就很麻煩,但是屏蔽這個類用ImportSelector去做就很容易了。下次如果需要用到了,我再放開這個開關(guān),直接可以使用MyDao的功能了,這樣就做到了一個靈活的功能掌控。
- 改造例子
其實Spring中那么多的EnableXXXX的注解底層就是這樣的原理
五、ImportSelector源碼分析
我們可以來看一下ConfigurationClassParser這個類的processImports方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {if (importCandidates.isEmpty()) {return;}if (checkForCircularImports && isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {this.importStack.push(configClass);try {for (SourceClass candidate : importCandidates) { //對ImportSelector的處理if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { //如果為延遲導(dǎo)入處理則加入集合當(dāng)中this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));}else { //根據(jù)ImportSelector方法的返回值來進行遞歸操作String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}}else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}else { // 如果當(dāng)前的類既不是ImportSelector也不是ImportBeanDefinitionRegistar就進行@Configuration的解析處理// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass));}}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configClass.getMetadata().getClassName() + "]", ex);}finally {this.importStack.pop();}}}在這里我們可以看到ImportSelector接口的返回值會遞歸進行解析,把解析到的類全名按照@Configuration進行處理
六、連帶說一下DeferredImportSelector
這個是看Spring源碼的時候發(fā)現(xiàn)的,直接翻譯就是延時加載ImportSelector,實現(xiàn)這個接口的類,將會在@Configuration后面被加載,用法什么的和ImportSelector功能基本一樣。因為用的比較稀有就不多做解釋了,僅僅作為一個只是擴展點介紹下。在ConfigurationClassParser中會有一個判斷,是不是這個接口,如果是就會放到后面解析。以下摘自源碼:
org.springframework.context.annotation.ConfigurationClassParser#processImports //這里攔截了DeferredImportSelector然后使用handle() if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); }進入handle()方法,發(fā)現(xiàn)和這個接口相關(guān)的都被加入了一個deferredImportSelectors的list中。
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);if (this.deferredImportSelectors == null) {DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();handler.register(holder);handler.processGroupImports();}else {//加入到了一個ArrayList中this.deferredImportSelectors.add(holder);} }最終這個ArrayList在parse()方法的最后被處理了
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)public void parse(Set<BeanDefinitionHolder> configCandidates) {//根據(jù)BeanDefinition的類型做不同的處理,一般都會調(diào)用ConfigurationClassParser.parse()進行解析for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition(); //拿出BeanDefinitiontry {if (bd instanceof AnnotatedBeanDefinition) { // 判斷是不是加了注解的// 解析注解對象,并且把解析出來的bd方法map中,但是這里的bd指的的普通的// 普通和不普通的怎么區(qū)分。比如@Bean和各種beanFactoryPostProcessor得到的bean//如果被加了注解,又調(diào)用了一個parse()方法parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}//處理,而此時上面其他的Import已經(jīng)處理完了this.deferredImportSelectorHandler.process(); } 與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Spring.ImportSelector接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020年A股IPO市场全景回顾与202
- 下一篇: 互联网日报 | 小米11取消随机附送充电