SpringBoot 的 @Import 用于將指定的類實例注入之Spring IOC Container中。
今天抽空在仔細看了下Springboot 關于 @Import 的處理過程, 記下來以后看。
1. @Import
先看Spring對它的注釋 (文檔貼過來的), 總結下來作用就是和xml配置的 <import />標簽作用一樣,允許通過它引入 @Configuration 注解的類 (java config), 引入ImportSelector接口(這個比較重要, 因為要通過它去判定要引入哪些@Configuration) 和 ImportBeanDefinitionRegistrar 接口的實現, 也包括 @Component注解的普通類。
但是如果要引入另一個xml 文件形式配置的 bean, 則需要通過 @ImportResource 注解。
/**
* 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. ImportSelector
因為 @Import 的實現有很多時候需要借助 ImportSelector 接口, 所以我們再看下這個接口的描述, 總結下來就是需要通過這個接口的實現去決定要引入哪些 @Configuration。 它如果實現了以下四個Aware 接口, 那么spring保證會在調用它之前先調用Aware接口的方法。
至于為什么要保證調用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注解的處理過程
Springboot對注解的處理都發生在AbstractApplicationContext -> refresh() -> invokeBeanFactoryPostProcessors(beanFactory) -> ConfigurationClassPostProcessor -> postProcessBeanDefinitionRegistry()方法中。
(稍微說下也免得我自己忘了, springboot初始化的普通context(非web) 是AnnotationConfigApplicationContext, 在初始化的時候會初始化兩個工具類, AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 分別用來從 annotation driven 的配置和xml的配置中讀取beanDefinition并向context注冊, 那么在初始化 AnnotatedBeanDefinitionReader 的時候, 會向BeanFactory注冊一個ConfigurationClassPostProcessor 用來處理所有的基于annotation的bean, 這個ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的一個實現,springboot會保證在 invokeBeanFactoryPostProcessors(beanFactory) 方法中調用注冊到它上邊的所有的BeanFactoryPostProcessor)
如下代碼顯示是通過 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();
}
}
}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。