Springboot @Import 详解

网友投稿 363 2023-01-18


Springboot @Import 详解

SpringBoot 的 @Import 用于将指定的类实例注入之Spring IOC Container中。

今天抽空在仔细看了下Springboot 关于 @Import 的处理过程, 记下来以后看。

1. @Import

先看Spring对它的注释 (文档贴过来的), 总结下来作用就是和xml配置的 标签作用一样,允许通过它引入 @Configuration 注解的类 (java config), 引入ImportSelector接口(这个比较重要, 因为要通过它去判定要引入哪些@Configuration) 和 ImportBeanDefinitionRegistrar 接口的实现, 也包括 @Component注解的普通类。

但是如果要引入另一个xml 文件形式配置的 bean, 则需要通过 @ImportResource 注解。

/**

* Indicates one or more {@link Configuration @Configuration} classes to import.

*

*

Provides functionality equivalent to the {@code } 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}).

*

*

{@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.

*

*

May be declared at the class level or as a meta-annotation.

*

*

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}jRzlmRcx

* class(es) should be imported based on a given selection criteria, usually one or more

* annotation attributes.

*

*

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}:

*

*

*

*

*

*

*

*

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 用来处理所jRzlmRcx有的基于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 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 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();

}

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:接口文档在线管理工具(在线api接口文档系统)
下一篇:实现接口和类 飞行类例子(实现类转换成接口类)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~