今天抽空在仔細看了下Springboot 關於 @Import 的處理過程, 記下來之後看。java
1. @Importweb
先看Spring對它的註釋 (文檔貼過來的), 總結下來做用就是和xml配置的 <import />標籤做用同樣,容許經過它引入 @Configuration 註解的類 (java config), 引入ImportSelector接口(這個比較重要, 由於要經過它去斷定要引入哪些@Configuration) 和 ImportBeanDefinitionRegistrar 接口的實現, 也包括 @Component註解的普通類。spring
可是若是要引入另外一個xml 文件形式配置的 bean, 則須要經過 @ImportResource 註解。springboot
/** * Indicates one or more {@link Configuration @Configuration} classes to import. * * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML. * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}). * * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} * injection. Either the bean itself can be autowired, or the configuration class instance * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly * navigation between {@code @Configuration} class methods. * * <p>May be declared at the class level or as a meta-annotation. * * <p>If XML or other non-{@code @Configuration} bean definition resources need to be * imported, use the {@link ImportResource @ImportResource} annotation instead. * * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Configuration * @see ImportSelector * @see ImportResource */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class<?>[] value(); }
2. ImportSelectorapp
由於 @Import 的實現有不少時候須要藉助 ImportSelector 接口, 因此咱們再看下這個接口的描述, 總結下來就是須要經過這個接口的實現去決定要引入哪些 @Configuration。 它若是實現瞭如下四個Aware 接口, 那麼spring保證會在調用它以前先調用Aware接口的方法。ide
至於爲何要保證調用Aware, 我我的以爲應該是你能夠經過這些Aware去感知系統裏邊全部的環境變量, 從而決定你具體的選擇邏輯。工具
/** * 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); }
3. Springboot 對@Import註解的處理過程post
Springboot對註解的處理都發生在AbstractApplicationContext -> refresh() -> invokeBeanFactoryPostProcessors(beanFactory) -> ConfigurationClassPostProcessor -> postProcessBeanDefinitionRegistry()方法中。 ui
(稍微說下也省得我本身忘了, springboot初始化的普通context(非web) 是AnnotationConfigApplicationContext, 在初始化的時候會初始化兩個工具類, AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 分別用來從 annotation driven 的配置和xml的配置中讀取beanDefinition並向context註冊, 那麼在初始化 AnnotatedBeanDefinitionReader 的時候, 會向BeanFactory註冊一個ConfigurationClassPostProcessor 用來處理全部的基於annotation的bean, 這個ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的一個實現,springboot會保證在 invokeBeanFactoryPostProcessors(beanFactory) 方法中調用註冊到它上邊的全部的BeanFactoryPostProcessor)this
以下代碼顯示是經過 ConfigurationClassParser 類來轉換的
// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
那麼在 ConfigurationClassParser -> processConfigurationClass() -> doProcessConfigurationClass() 方法中咱們找到了 (這裏邊的流程仍是很清楚的, 分別按次序處理了@PropertySource, @ComponentScan, @Import, @ImportResource, 在處理這些註解的時候是經過遞歸處理來保證全部的都被處理了)
// Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true);
那接下來就看它究竟是怎麼作的 . 流程依然清晰 :
首先, 判斷若是被import的是 ImportSelector.class 接口的實現, 那麼初始化這個被Import的類, 而後調用它的selectImports方法去得到所須要的引入的configuration, 而後遞歸處理
其次, 判斷若是被import的是 ImportBeanDefinitionRegistrar 接口的實現, 那麼初始化後將對當前對象的處理委託給這個ImportBeanDefinitionRegistrar (不是特別明白, 只是個人猜想)
最後, 將import引入的類做爲一個正常的類來處理 ( 調用最外層的doProcessConfigurationClass())
因此, 從這裏咱們知道, 若是你引入的是一個正常的component, 那麼會做爲@Compoent或者@Configuration來處理, 這樣在BeanFactory裏邊能夠經過getBean拿到, 但若是你是 ImportSelector 或者 ImportBeanDefinitionRegistrar 接口的實現, 那麼spring並不會將他們註冊到beanFactory中,而只是調用他們的方法。
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) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> 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) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { 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 definitions Class<?> 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 { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.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(); } } }