Spring Boot 注解——@Import

引言

Import 见名知意:导入某某东西。在平时看源码或者很多配置类上面都会出现@Import注解,功能就是和Spring XML 里面的一样。@Import注解是用来导入配置类或者一些需要前置加载的类

使用方法

  1. 被标注@Configuration的配置类,@Configuration详细用法及作用请参考 Spring Boot注解——@Configuration
    @Configuration
    public class DatabaseConfig {
    	@Bean
    	public DataSource dataSource() {
    		// instantiate, configure and return DataSource
    	}
    }
    
    @Configuration
    @Import(DatabaseConfig.class)
    public class AppConfig {
    	private final DatabaseConfig dataConfig;
    
    	public AppConfig(DatabaseConfig dataConfig) {
    		this.dataConfig = dataConfig;
    	}
    
    	@Bean
    	public MyBean myBean() {
    		// reference the dataSource() bean method
    		return new MyBean(dataConfig.dataSource());
    	}
    }
    
  2. 实现ImportSelector接口
  3. 实现ImportBeanDefinitionRegistrar接口
  4. 在标注@Componment@Service@Controller等一系列能够注入容器的注解的类,即常规组件类 (从 4.2 开始;类似于AnnotationConfigApplicationContext.register()

源码解析

// 所在的包
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
  * 指示要导入的一个或多个组件类——通常是@Configuration类。提供与 Spring XML 中的<import/>元素等效的功能。允许
  * 导入@Configuration类、 ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从 4.2 开始;
  * 类似于AnnotationConfigApplicationContext.register)。在导入的@Configuration类中声明的@Bean定义可以使用
  * @Autowired来访问。bean本身可以自动装配,或者声明 bean 的配置类实例可以自动装配。后一种方法允许在
  * @Configuration类方法之间进行显式的、IDE 友好的导航。可以在类级别声明或作为元注释声明。
  * 如果需要导入 XML 或其他非@Configuration bean 定义资源,请改用@ImportResource注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * 需要导入的资源,可以是:
	 * @Configuration标注的配置类
	 * ImportSelector接口具体实现
	 * ImportBeanDefinitionRegistrar接口具体实现
	 * 常规组件类
	 */
	Class<?>[] value();
}

实现逻辑

以下内容引自一直打铁 ---- @Import注解的作用

这里主要看 ConfigurationClassParser.java 里的
doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)这个方法。具体定位到源码的310行代码(为啥不从头梳理,这里Spring 启动过程比较复杂,要是从头梳理,涉及的东西比较多,好多人看了就会累,放弃了,我们就单个点熟悉 ,最后再进行汇总)
image

根据程序的逻辑,肯定是先执行getImports(sourceClass)

getImports() 方法

在分析这个方法之前,我们先看一下 getImports 方法,这个方法就是获取所有的@import里面的类
这里是获取@import里面的类,大致流程如下:

  1. 定义一个visited的集合,用作是否已经判断过的标志
  2. 这里就是获取sourceClass上面的所有的annotation,并挨个判断, 如果不是@import,那就 进一步递归调用对应的annotation,直到全部结束
  3. 加载sourceClass里面的@Import annotation里面对应的类名,最后返回。
    private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    	Set<SourceClass> imports = new LinkedHashSet<>();
    	Set<SourceClass> visited = new LinkedHashSet<>();
    	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();
    			if (!annName.equals(Import.class.getName())) {
    				collectImports(annotation, imports, visited);
    			}
    		}
    		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    	}
    }
    

processImports() 方法

image
大致的流程如下:

  1. 判断importCandidates是否为空,为空 退出
  2. 判断isChainedImportOnStack,如果为true,加入problemReporter里面的error,并退出
  3. 把当前的configClass加入到 ImportStack里面,ImportStack 是继承了 ArrayDeque和实现了 ImportRegistry
  4. getImports 里面获取到的 需要import 的类 进行遍历 处理
    • 如果是 ImportSelector 类型,首先实例一个 ImportSelector 对象,然后 对其进行 Aware 扩展(如果 实现了 Aware 接口)
    • 进一步判断 是否 是 DeferredImportSelector 类型,如果是 ,加入到 deferredImportSelectors 里面,最后处理 ,这里可以看一下 方法parse(Set configCandidates), 里面最后一行才调用,这也就是 有的时候,如果想最后注入,就可以定义为deferredImportSelectors类型
    • 如果 不是DeferredImportSelector类型 ,那就 调用selectImports方法,获取到所有的需要 注入的类,这时 再次调用processImports方法,这里调用processImports方法,其实是把这些需要注入的类当成普通的@Configuration处理
  5. 如果是ImportBeanDefinitionRegistrar类型,这里也是 先实例一个对象,然后加入到importBeanDefinitionRegistrars里面,后续会在ConfigurationClassBeanDefinitionReader这个类里面的loadBeanDefinitionsFromRegistrars方法处理的
  6. 如果上面两种类型都不是,那就是当初普通的 带有@Configuration的类进行处理了
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)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					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);
					}
					if (selector instanceof DeferredImportSelector) {
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, 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 =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									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), 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();
		}
	}
}
posted @ 2022-04-13 09:52  夏醉浅梦  阅读(551)  评论(0)    收藏  举报