本文是Spring IoC容器技术介绍系列文章之一。本文介绍Spring IoC中的@Import
注解。
功能解释
@Import annotation allows for loading @Bean definitions from another configuration class
@Import
注解支持从其他的配置类中加载@Bean
的定义。
一般通过在程序中使用配置类的方式定义所有的bean对象,但当我们希望使用工程中引入的其他包的配置类时,@Import
注解就非常有用了。它可以帮助我们在不改变外部包的同时,引入别人定义好的配置类,进而加载这些配置类里面的bean对象。
使用示例
@Import(AnotherConfiguration.class)
public class Application {
static class AnotherConfiguration {
@Bean NumberStyleFormatter numberStyleFormatter() {
return new NumberStyleFormatter();
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
AnotherConfiguration configuration = applicationContext.getBean(AnotherConfiguration.class);
NumberStyleFormatter formatter = applicationContext.getBean(NumberStyleFormatter.class);
System.out.println(configuration.getClass().getName());
System.out.println(formatter.getClass().getName());
}
}
上面这段代码展示了如何使用@Import
注解定义bean对象。
Application
类中定义了一个名为AnotherConfiguration
的内部类。定义成内部类的原因是为了代码长度尽可能短,便于展示。@Import
引入的类,不需要非是当前类的内部类,也不需要在当前类的相同package下。
AnotherConfiguration
中定义了一个NumberStyleFormatter
类型的@Bean
。
在Application
上,使用了@Import(AnotherConfiguration.class)
注解。
在main()
函数中,在容器启动后拿到AnotherConfiguration
类型和NumberStyleFormatter
类型的bean,并打印他们的名字。
程序执行情况
ImportSelector
ImportSelector
是Spring Context中定义的接口。该接口有两个方法:
-
String[] selectImports(...)
:返回需要引入的配置类的名称数组。 -
Predicate<String> getExclusionFilter()
:返回哪些配置类需要过滤掉。该方法的返回是Predicate<String>
对象。Predicate<String>
是一个接收String
类型参数作为入参,返回Boolean
类型结果的方法。
ImportSelector继承图
如果@Import
注解引入的对象是一个ImportSelector
类型的类的话,则该类的String[] selectImports(...)
方法最终会被调用,这个方法返回的String
类型数组中的每一个元素,都会作为配置类被加载。
另外,该类的Predicate<String> getExclusionFilter()
方法返回的Predicate<String>
会被作为过滤函数。在该类引入的所有配置类进行解析时,使用它对这些配置类进行过滤。
ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar
是在Spring Context中定义的,另一个和@Import
配合使用,用于初始化IoC容器的接口。
该接口中定义了两个registerBeanDefinitions(...)
方法。IoC容器在初始化时,如果@Import
引入的对象是ImportBeanDefinitionRegistrar
的某个具体子类,则registerBeanDefinitions(...)
方法会被调起。registerBeanDefinitions(...)
方法会将相关的@Bean
定义写入到BeanDefinitionRegistry
类型的入参中。
ImportBeanDefinitionRegistrar继承链
registerBeanDefinitions(...)
方法中传入的BeanDefinitionRegistry
类型的入参,其实就是ApplicationContext
对象内部的beanFactory
属性。这个beanFactory
属性的类型是DefaultListableBeanFactory
,而DefaultListableBeanFactory
实现了BeanDefinitionRegistry
接口。
BeanDefinitionRegistry的继承链
源码解释
@Import
注解的处理在ConfigurationClassParser
类中。ConfigurationClassParser
是Spring框架用于处理配置类的核心对象,processImports(...)
方法是处理@Import
注解的核心方法:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
...
if (...) {
...
}
else {
...
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
...
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
...
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
....
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
...
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (...) {
...
}
...
}
}
processImports(...)
中,主要根据candidate
的类型的不同,做不一样的操作处理,具体的处理逻辑可以参考上面章节的相关介绍。
总结
@Import
是Spring中用于引入其他配置类的注解,主要分为三种场景:
-
引入配置类是
ImportSelector
类型的对象,则调用其selectImports(...)
方法,得到一组配置类的清单,再对清单中的每个配置类分别处理,加载相关@Bean
定义; -
引入配置类是
ImportBeanDefinitionRegistrar
类型的对象,则调用其registerBeanDefinitions(...)
方法,完成@Bean
定义的处理; -
其他类型的对象,则直接作为正常配置类解析,加载相关
@Bean
定义。