本文基于 Spring 5.2.x
@Import
注解
@Import
是Spring
基于 Java 注解配置的主要组成部分。@Import
注解提供了@Bean
注解的功能,同时还有原来Spring
基于 xml 配置文件里的<import>
标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration
的类。
下面将分别说明@Import
注解的功能。
1. 引入其他的@Configuration
假设有如下接口和两个实现类:
package com.test
interface ServiceInterface {
void test();
}
class ServiceA implements ServiceInterface {
@Override
public void test() {
System.out.println("ServiceA");
}
}
class ServiceB implements ServiceInterface {
@Override
public void test() {
System.out.println("ServiceB");
}
}
两个@Configuration
,其中ConfigA``@Import``ConfigB
:
package com.test
@Import(ConfigB.class)
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
@Configuration
class ConfigB {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceB() {
return new ServiceB();
}
}
通过ConfigA
创建AnnotationConfigApplicationContext
,获取ServiceInterface
,看是哪种实现:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
ServiceInterface bean = ctx.getBean(ServiceInterface.class);
bean.test();
}
输出为:ServiceB
.证明@Import
的优先于本身的的类定义加载。
2. 直接初始化其他类的Bean
在Spring 4.2之后,@Import
可以直接指定实体类,加载这个类定义到context
中。 例如把上面代码中的ConfigA
的@Import
修改为@Import(ServiceB.class)
,就会生成ServiceB
的Bean
到容器上下文中,之后运行main
方法,输出为:ServiceB
.证明@Import
的优先于本身的的类定义加载.
3. 指定实现ImportSelector
(以及DefferredServiceImportSelector
)的类,用于个性化加载
指定实现ImportSelector
的类,通过AnnotationMetadata
里面的属性,动态加载类。AnnotationMetadata
是Import
注解所在的类属性(如果所在类是注解类,则延伸至应用这个注解类的非注解类为止)。
需要实现selectImports
方法,返回要加载的@Configuation
或者具体Bean
类的全限定名的String
数组。
package com.test;
class ServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//可以是@Configuration注解修饰的类,也可以是具体的Bean类的全限定名称
return new String[]{"com.test.ConfigB"};
}
}
@Import(ServiceImportSelector.class)
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
再次运行main
方法,输出:ServiceB
.证明@Import
的优先于本身的的类定义加载。 一般的,框架中如果基于AnnotationMetadata
的参数实现动态加载类,一般会写一个额外的Enable
注解,配合使用。例如:
package com.test;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportSelector.class)
@interface EnableService {
String name();
}
class ServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//这里的importingClassMetadata针对的是使用@EnableService的非注解类
//因为`AnnotationMetadata`是`Import`注解所在的类属性,如果所在类是注解类,则延伸至应用这个注解类的非注解类为止
Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
if (Objects.equals(name, "B")) {
return new String[]{"com.test.ConfigB"};
}
return new String[0];
}
}
之后,在ConfigA
中增加注解@EnableService(name = "B")
package com.test;
@EnableService(name = "B")
@Configuration
class ConfigA {
@Bean
@ConditionalOnMissingBean
public ServiceInterface getServiceA() {
return new ServiceA();
}
}
再次运行main
方法,输出:ServiceB
.
还可以实现DeferredImportSelector
接口,这样selectImports
返回的类就都是最后加载的,而不是像@Import
注解那样,先加载。 例如:
package com.test;
class DefferredServiceImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
if (Objects.equals(name, "B")) {
return new String[]{"com.test.ConfigB"};
}
return new String[0];
}
}
修改EnableService
注解:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(DefferredServiceImportSelector.class)
@interface EnableService {
String name();
}
这样ConfigA
就优先于DefferredServiceImportSelector
返回的ConfigB
加载,执行main
方法,输出:ServiceA
4. 指定实现ImportBeanDefinitionRegistrar
的类,用于个性化加载
与ImportSelector
用法与用途类似,但是如果我们想重定义Bean
,例如动态注入属性,改变Bean
的类型和Scope
等等,就需要通过指定实现ImportBeanDefinitionRegistrar
的类实现。例如:
定义ServiceC
package com.test;
class ServiceC implements ServiceInterface {
private final String name;
ServiceC(String name) {
this.name = name;
}
@Override
public void test() {
System.out.println(name);
}
}
定义ServiceImportBeanDefinitionRegistrar
动态注册ServiceC
,修改EnableService
package com.test;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ServiceImportBeanDefinitionRegistrar.class)
@interface EnableService {
String name();
}
class ServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableService.class.getName(), true);
String name = (String) map.get("name");
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(ServiceC.class)
//增加构造参数
.addConstructorArgValue(name);
//注册Bean
registry.registerBeanDefinition("serviceC", beanDefinitionBuilder.getBeanDefinition());
}
}
并且根据后面的源代码解析可以知道,ImportBeanDefinitionRegistrar
在 @Bean
注解之后加载,所以要修改ConfigA
去掉其中被@ConditionalOnMissingBean
注解的Bean
,否则一定会生成ConfigA
的ServiceInterface
package com.test;
@EnableService(name = "TestServiceC")
@Configuration
class ConfigA {
// @Bean
// @ConditionalOnMissingBean
// public ServiceInterface getServiceA() {
// return new ServiceA();
// }
}
之后运行main
,输出:TestServiceC
@Import
相关源码解析
加载解析@Import
注解位于BeanFactoryPostProcessor
处理的时候:
AbstractApplicationContext
的refresh
方法
-> invokeBeanFactoryPostProcessors(beanFactory);
-> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-> registryProcessor.postProcessBeanDefinitionRegistry(registry);
这里的registryProcessor
,我们指ConfigurationClassPostProcessor
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(registry)
-> processConfigBeanDefinitions(registry)
:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略一些配置检查与设置的逻辑
//根据@Order注解,排序所有的@Configuration类
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 创建ConfigurationClassParser解析@Configuration类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
//剩余没有解析的@Configuration类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
//已经解析的@Configuration类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//解析
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 生成类定义读取器读取类定义
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
//省略检查是否有其他需要加载的配置的逻辑
}
}
while (!candidates.isEmpty());
//省略后续清理逻辑
}
其中parser.parse(candidates)
的逻辑主要由org.springframework.context.annotation.ConfigurationClassParser
实现,功能是加载@Import
注解还有即系@Import
注解。reader.loadBeanDefinitions(configClasses);
的逻辑主要由org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader
的loadBeanDefinitionsForConfigurationClass
方法实现,功能是将上面解析的配置转换为BeanDefinition
就是Bean
定义。
1. 加载@Import
注解
org.springframework.context.annotation.ConfigurationClassParser
首先是parse
方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
//这里的parse实际上就是调用下面即将分析的doProcessConfigurationClass
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);
}
}
//最后处理所有的`DeferredImportSelector`,符合上面提到的`DeferredImportSelector`的功能
this.deferredImportSelectorHandler.process();
}
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//处理`@Component`注解的MemberClass相关代码...
//处理`@PropertySource`注解相关代码...
//处理`@ComponentScan`注解相关代码...
//处理`@Import`注解:
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//处理`@ImportResource`注解相关代码...
//处理`@Bean`注解相关代码...
//处理接口方法相关代码...
//处理父类相关代码...
}
通过getImports
方法,采集相关的@Import
里面的类。
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
//递归查询所有注解以及注解的注解是否包含@Import
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
//记录是否已经扫描过这个类,如果扫描过就不重复添加,防止重复或者死循环
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
//对于非@Import注解,递归查找其内部是否包含@Import注解
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
//添加@Import注解里面的所有配置类
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
采集好之后,就可以解析了。
2. 解析@Import
注解
解析的方法是:processImports
//在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖
private final ImportStack importStack = new ImportStack();
//记录所有的ImportBeanDefinitionRegistrar
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
//解析也是递归方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
//通过importStack检查循环依赖
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)) {
//处理ImportSelector接口的实现类
Class<?> candidateClass = candidate.loadClass();
//创建这些Selector实例
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);
}
//如果是DeferredImportSelector,则用deferredImportSelectorHandler处理
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
//如果不是DeferredImportSelector,调用selectImports方法获取要加载的类全限定名称,递归调用本方法继续解析
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 处理ImportBeanDefinitionRegistrar接口的实现类
Class<?> candidateClass = candidate.loadClass();
//同样的,创建这些ImportBeanDefinitionRegistrar实例
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//放入importBeanDefinitionRegistrar,用于后面加载
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
//处理@Configuration注解类,或者是普通类(直接生成Bean)
//在栈加上这个类
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//递归回到doProcessConfigurationClass处理@Configuration注解类 processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
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();
}
}
}
这样,所有的@Conditional
类相关的@Import
注解就加载解析完成了,这是一个大的递归过程。
3. 转换为BeanDefinition
注册到容器
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader
的loadBeanDefinitionsForConfigurationClass
方法:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//对Import完成的,加载其Import的BeanDefinition
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//加载@Bean注解的方法生成的Bean的Definition
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//@ImportResource 注解加载的
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//加载ImportBeanDefinitionRegistrar加载的Bean的Definition
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
通过这里可以看出,为啥之前说@Bean
注解的Bean
会优先于ImportBeanDefinitionRegistrar
返回的Bean
加载。