0
点赞
收藏
分享

微信扫一扫

第12章 Spring的Import注解深入分析

冬冬_79d4 2022-04-04 阅读 54

Import注解用来导入Bean对象到Spring容器中,最常用就是导入被 @Configuration 标识的配置类。@Import 注解允许导入 @Configuration 类、ImportSelectorImportBeanDefinitionRegister的实现类。

使用

导入普通类

定义 Car 类,然后通过 @Import 注解把 Car 导入Spring容器中,无须手动注入它。

public class Car {   
}

定义配置类,使用 @Import 注解标识。注意:即时配置类没有被 @Configuration 标识,被 @Import注解标识的类也会被Spring当成配置类处理。

@Import(Car.class)
public class AccountConfig3 {
}

测试

public class AnnotationConfigTest {
	public static void main(String[] args) throws IOException {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AccountConfig3.class);
        Car bean = context.getBean(Car.class);
		System.out.println(bean);
    }
}
ImportSelector的方式导入
public class TestImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.test.annotationConfig.entity.TestBean1"};
	}
}
ImportBeanDefinitionRegistrar的方式导入
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
		ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry, importBeanNameGenerator);
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestBean2.class);
		registry.registerBeanDefinition("testBean2", rootBeanDefinition);
	}
}

源码分析

Spring是如何处理 @Import 注解的?这些Bean对象又是如何导入Spring容器中的呢?

ConfigurationClassParser 类是用来解析配置类,这里通过简化一些方法,目的理解Spring是如何处理配置类以及 @Import 的处理。该类的方法processConfigBeanDefinitions就是处理配置类的入口。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // 获取已经注册BeanDef
    String[] candidateNames = registry.getBeanDefinitionNames();
    for (String beanName : candidateNames) {
        // 如果BeanDef是带有@Configuration注解
        //或者是@Configuration候选注解(Component、ComponentScan、Import、ImportResource)也看作是配置类
        if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
           		 //加入配置类集
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
    }
    
    // 构建 ConfigurationClassParser对象类解析配置类,包括:@ComponentScan、
    // @Bean、@Import、@PropertySource注解处理,包括递归处理配置类
    ConfigurationClassParser parser = new ConfigurationClassParser(......);
    
    do {
        parser.parse(candidates);
        // 获取已经解析完的配置类
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        
        // 构建 ConfigurationClassBeanDefinitionReader 对象用来加载配置类配置的Bean对象
        this.reader.loadBeanDefinitions(configClasses);
    } while (!candidates.isEmpty());
}
ConfigurationClassParser

ConfigurationClassParser 负责解析@ComponentScan、@Bean、@Import、@PropertySource注解处理,包括递归处理配置类。这里简化一些方法,只看@Import注解的处理。由方法 processConfigurationClass 来处理。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    
    // ....
    // 把配置类当做类来处理
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
	}
	while (sourceClass != null);
    
    // 处理完成的配置类放入 Map对象中
    this.configurationClasses.put(configClass, configClass);
}

doProcessConfigurationClass 是真正负责解析@ComponentScan、@Bean、@Import、@PropertySource等注解。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
    // ...
    
    // 处理@Import注解
    // getImports方法用来获取 @Import注解的值
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
}

这里开始处理 @Import ,主要分三种情况:

  • ImportSelector 子类:如果导入的类实现了 ImportSelector 接口,获取需要导入的类名,然后递归处理。
  • ImportBeanDefinitionRegistrar 子类:实例化该类,并把它放入配置类的Map中
  • 普通类:当作配置类来处理,递归处理配置类。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {

    // 遍历从@Import获取值,即导入的Bean
    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
            // 伪代码,实例化 candidate 类
            ImportSelector selector = instantiateClass();
            // 获取importClassName
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            // 然后递归处理
            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
        }
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // 实例化candidate类
            ImportBeanDefinitionRegistrar registrar = instantiateClass();
            // 把实例化的candidate类放入到配置类的Map中。
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // 如果导入的Bean不属于前两个接口的实现类的话,把这个Bean当做配置类来处理.
            // asConfigClass() 会创建新的配置,并注明新的配置类candidate是由configClass导入的
            // importedBy --> configClass
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}
ConfigurationClassBeanDefinitionReader

当配置解析完成后,需要由 ConfigurationClassBeanDefinitionReader 类来配置类配置的一些BeanDefinition。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    	// 遍历配置类,从配置类中加载BeanDefinition
		for (ConfigurationClass configClass : configurationModel) {
			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
		}
	}
private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

		// 省略其他代码....
    
    	// 这个配置类是由其他配置通过 @Import注解导入的
		if (configClass.isImported()) {
            // 这就是为什么 @Import注解可以导入普通类的原因。例如 @Import(Test.class)
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            // 加载由@Bean 注解的BeanDefiniton
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

    	// 加载类从 xml或者其他文件
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
			// 加载BeanDefintion 从 ImportBeanDefinitionRegistrar 接口
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

参考

  • Spring @Import Annotation
举报

相关推荐

0 条评论