@Import注解的使用和原理

2,244 阅读6分钟

一:简述

@Import注解是Spring中比较核心的注解,它的功能很强大,特别是在SpringBoot中,@Import注解使用的地方非常多,无论是@EnableXX类型的注解,还是自动装配都和@Import注解脱不开关系。今天就和大家聊聊它的作用以及原理。

二:@Import注解的作用

@Import功能和Spring XML配置文件里面的<Import>标签一样,也就是用来把配置类或者一些需要加载的类加入到Spring IoC容器中。

而导入的类可以分为三种情况:

  1. 导入@Configuration的配置类或普通类(在Spring4.2之后支持导入普通类)
@Component
@Import(User.class)
public class ImportTest {
    
}
@Data
public class User {

    private Integer id;

    private String username;

    private String password;
}

这种情况下,@Import注解的作用就是将类加入到IoC容器中

  1. 导入 ImportSelector接口或 DeferredImportSelector接口的实现类

ImportSelector接口:

public class ImportSeletorTest implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.User"};
    }
}
@Component
@Import(ImportSeletorTest.class)
public class ImportTest {

}
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

DeferredImportSelector接口:

public class DeferredImportTest implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return null;
    }

    @Override
    public Class<? extends Group> getImportGroup() {
        return TestGroup.class;
    }

     private static class TestGroup implements Group {

        List<Entry> entries = new ArrayList<>();

        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            Entry entry = new Entry(metadata,"com.example.User");
            entries.add(entry);
        }

        @Override
        public Iterable<Entry> selectImports() {
            return entries;
        }
    }
}
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

这种情况下我们可以自定义逻辑,根据我们的业务逻辑判断导入一些类到IoC容器中。

  1. 导入 ImportBeanDefinitionRegistrar接口的实现类
public class RegistrarTest implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("user",new RootBeanDefinition(User.class));
    }
}
@Component
@Import(RegistrarTest.class)
public class ImportTest {

}
package com.example;

public class User {

    private Integer id;

    private String username;

    private String password;
}

如果@Import注解导入的类是ImportBeanDefinitionRegistrar的实现类,那么可以利用registerBeanDefinitions()方法将bean注入到IoC容器中。

注:DeferredImportSelector接口是ImportSelector接口的子接口,它们的区别在下文进行分析

三:源码分析

在ConfigurationClassPostProcessor这个处理器中,它实现了BeanDefinitionRegistryPostProcessor接口,所以对bean初始化之前会调用postProcessBeanDefinitionRegistry()方法,在postProcessBeanDefinitionRegistry()方法中对@Component,@ComponentScan,@Import,@ImportResource等注解进行了处理。调用路径是这样的:

未命名文件.png

对于@Import注解的处理是在ConfigurationClassParser类的processImports()方法中,所以我们重点分析processImports()方法。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
                        
	        ...... 
                
                //由于篇幅原因前面代码省略 重点看processImports()方法
                
                //处理@Import注解
		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
                
                //省略
                .....
               
	}

流程图:

Import注解处理流程.png

1. 如果是ImportSelector类型

a. 进一步判断是否是DeferredImportSelector类型,如果是,加入到deferredImportSelectors里面最后处理,在parse()方法,最后一行才调用deferredImportSelectorHandler.process()方法进行处理。最后会调用process()方法和group的electImports()获取到需要导入的类,然后再次调用processImports方法。

b. 如果不是DeferredImportSelector类型,那就调用selectImports方法,获取到所有的需要注入的类,这时再次调用processImports方法。

2. 如果是 ImportBeanDefinitionRegistrar 类型,这里也是 先实例一个对象,然后加入到 importBeanDefinitionRegistrars 里面,后续会在ConfigurationClassBeanDefinitionReader这个类里面的loadBeanDefinitionsFromRegistrars方法处理的

3. 如果既不是ImportSelector类型也不是ImportBeanDefinitionRegistrar类型,就再调用processConfigurationClass()方法进行处理。

源码:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			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)) {
                                        //处理ImportSelector的实现类
						Class<?> candidateClass = candidate.loadClass();
						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的handle方法
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
                                                       //调用selectImports()方法获取需要导入的类 排除需要排除的类之后 递归调用一下processImports
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					}
                                        // 如果是导入实现了ImportBeanDefinitionRegistrar的类
					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 =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                                                //对于配置类和普通类调用processConfigurationClass进行处理
						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();
			}
		}
	}

四:DeferredImportSelector和ImportSelector的区别

根据前文我们已经知道了DeferredImportSelector是ImportSelector的子接口,它们二者最主要的区别在于DeferredImportSelector接口的实现类会在最后进行处理(在@Bean,@ImportResource等注解之后)。在processImports()方法处理DeferredImportSelector方法的时候会调用 deferredImportSelectorHandler.handle()进行处理。我们可以看到deferredImportSelectors在类中已经new了,所以第一次进来肯定不为null,所以只会先放入到deferredImportSelectors,等之后处理。

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
			//第一次进来肯定不为null 只有在调用deferredImportSelectorHandler.process()之后才会为null
			if (this.deferredImportSelectors == null) {
				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
				handler.register(holder);
				handler.processGroupImports();
			}
			else {
                                //放入到deferredImportSelectors中
				this.deferredImportSelectors.add(holder);
			}
		}

在parse()方法中,我们可以明显看到DeferredImportSelector是最后处理。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					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导入的类
		this.deferredImportSelectorHandler.process();
	}

最后分析下DeferredImportSelector类的处理,它首先会获取DeferredImportSelector类的getImportGroup()返回的Group,如果返回的Group是null(默认实现是返回null),就会使用默认的Group(也就是DefaultDeferredImportSelectorGroup),然后会分别调用Group的process()方法和selectImports()方法收集需要导入的类,最后会调用processImports()方法将收集的类导入到Spring容器中。

注:SpringBoot的自动装配和DeferredImportSelector类是脱不开关系的,我们理解了DeferredImportSelector,那么自动装配的原理也就懂了一大半了。

源码:

deferredImportSelectorHandler的process()方法负责处理导入DeferredImportSelector类的处理逻辑,首先循环收集的DeferredImportSelectorHolder,调用它的register方法将DeferredImportSelector进行分组,然后调用processGroupImports()方法分组进行处理。

process()

public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			//将deferredImportSelectors设置为null 说明已经到了进行deferredImportSelectors处理的阶段了
			// 下次就不需要在延迟处理了
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					//循环调用register方法
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
	}

register()

register()方法的作用是根据DeferredImportSelector的getImportSelector()方法将收集的DeferredImportSelector进行分组,并且保存在DeferredImportSelectorGrouping中。

public void register(DeferredImportSelectorHolder deferredImport) {
			//获取Group
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			//如果getImportGroup()方法返回的Group为null 那么就以DeferredImportSelectorHolder为key 并且用默认的DeferredImportSelectorGrouping为value
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			//将需要导入的类加入到configurationClasses中
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}

processGroupImports()

processGroupImports()方法作用是根据getImports()方法获取到需要导入的类,然后再次通过processImports()方法将需要导入的类导入到Spring容器中。

public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				//循环getImports()返回的Entry 获取需要导入的类
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
						//将需要导入的类再次通过processImports()方法进行处理
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
					}
				});
			}
		}

getImports()

getImports()方法通过Group的process和selectImports()方法收集需要导入的类。

public Iterable<Group.Entry> getImports() {
			//循环所有的deferredImports 调用Group的process()方法
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			//调用Group的selectImports() 并且返回收集到的Entry
			return this.group.selectImports();
		}

默认的Group:

DefaultDeferredImportSelectorGroup

private static class DefaultDeferredImportSelectorGroup implements Group {

		private final List<Entry> imports = new ArrayList<>();

		@Override
		public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
                        //调用ImportSelector的selectImports()方法收集需要导入的类
			for (String importClassName : selector.selectImports(metadata)) {
				this.imports.add(new Entry(metadata, importClassName));
			}
		}

		@Override
		public Iterable<Entry> selectImports() {
			return this.imports;
		}
	}