Spring源码学习笔记(7)——使用@Import导入组件

时间:2022-07-24
本文章向大家介绍Spring源码学习笔记(7)——使用@Import导入组件,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Spring源码学习笔记(7)——使用@Import导入组件

一. @Import注解简介

@Import注解的作用是导入其他的配置类或者组件,等同于在applicationContext.xml文件中添加如下配置

<import/>

在通过@Bean注解导入组件比较繁琐时,可以考虑通过@Import导入。

@Import支持三种方式的导入:

  1. 直接导入一个配置类或者Bean
  2. 导入ImportSelector的实现类
  3. 导入ImportBeanDefinitionRegistrar的实现类

下面分别介绍这几种方式。

二. 直接导入一个Bean或者Configuration

使用@Import注解可以直接向容器中注入一个普通的类:

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/21 10:15
 * @Description:Spring配置类
 */
@Configuration
@ComponentScan
@Import(Red.class)	//注入一个普通的Red类实例
public class MainConfig {
}

获取容器中的Red实例:

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/21 10:17
 * @Description:
 */
public class AnnotationMain {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Map<String, Red> redBeans = applicationContext.getBeansOfType(Red.class);
        if (!CollectionUtils.isEmpty(redBeans)){
            redBeans.forEach((name, redBean) -> System.err.println("name: " + name + ",bean: " + redBean));
        }
    }
}

结果如下:

name: william.annotation.entity.Red,bean: william.annotation.entity.Red@431cd9b2

可以看到,通过@Import注解向容器中导入了一个Red实例,该Bean的name是类的全限定类名。

三. 导入ImportSelector

ImportSelector是Spring提供的一个接口,通过实现该接口,可以指定哪些类需要被导入到IoC容器中。

下面自定义ImportSelector,导入指定类的实例:

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/28 11:40
 * @Description:自定义ImportSelector
 */
public class ColorImportSelector implements ImportSelector{
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //导入指定类的实例
        return new String[] {
                "william.annotation.entity.Blue",
                "william.annotation.entity.Red"
        };
    }
}

使用@Import导入自定义ImportSelector

@Configuration
@ComponentScan
@Import(ColorImportSelector.class)
public class MainConfig {
}

打印容器中所有Bean的name:

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/21 10:17
 * @Description:
 */
public class AnnotationMain {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        Arrays.stream(beanDefinitionNames).forEach(System.err::println);
    }
}

可看到控制台输出:

william.annotation.entity.Blue
william.annotation.entity.Red

通过@Import导入ImportSelector,可实现自定义Bean的注入,Bean的name同样为类的全限定类名。

四. 导入ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口的作用同ImportSelector类似,可以在BeanDefinition注册中心中注册额外的BeanDefinition。

首先自定义ImportBeanDefinitionRegistrar实现类,向注册中心中注册BeanDefinition对象

/**
 * @Auther: ZhangShenao
 * @Date: 2018/9/28 12:50
 * @Description:自定义ImportBeanDefinitionRegistrar,向IoC容器中注册额外的BeanDefinition
 */
public class RainbowImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
    /**
     * 向BeanDefinitionRegistry中注册自定义的BeanDefinition
     * @param importingClassMetadata @Import注解所在类的注解元信息
     * @param registry BeanDefinition注册中心,管理目前容器中所有的BeanDefinition
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition("william.annotation.entity.Blue") &&
                registry.containsBeanDefinition("william.annotation.entity.Red")){
            registry.registerBeanDefinition("rainbow",new RootBeanDefinition(Rainbow.class));
        }
    }
}

使用@Import注解导入ImportBeanDefinitionRegistrar

@Configuration
@ComponentScan
@Import({ColorImportSelector.class, RainbowImportBeanDefinitionRegistrar.class})
public class MainConfig {
}

打印容器中的Bean实例,可以看到Rainbow实例被注入到了容器中,BeanName是自定义的

william.annotation.entity.Blue
william.annotation.entity.Red
rainbow

五. @Import注解在SpringBoot中的应用

SpringBoot中大量使用了@Import注解导入组件,下面以SpringBoot的自动配置原理为例,介绍它的使用。

使用过SpringBoot进行开发的朋友们都很熟悉,通过@SpringBootApplication注解就可以将一个入口类标记为SpringBoot的启动类,然后就可以直接启动一个SpringBoot应用,SpringBoot会将项目所需的各种组件一次性自动注入到IoC容器中。这些自动配置功能是如何实现的呢?

打开@SpringBootApplication源码,可以看到:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 

可以看到一个注解:@EnableAutoConfiguration,SpringBoot正是通过这个注解开启了自动配置功能。查看@EnableAutoConfiguration的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

可以看到,@EnableAutoConfiguration使用了@Import注解,导入了AutoConfigurationImportSelector类,正是上面说的ImportSelector的实现类,导入了自动配置相关的组件。查看AutoConfigurationImportSelector的实现:

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {

	private static final String[] NO_IMPORTS = {};

	private static final Log logger = LogFactory
			.getLog(AutoConfigurationImportSelector.class);

	private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

	private ConfigurableListableBeanFactory beanFactory;

	private Environment environment;

	private ClassLoader beanClassLoader;

	private ResourceLoader resourceLoader;

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		try {
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			List<String> configurations = getCandidateConfigurations(annotationMetadata,
					attributes);
			configurations = removeDuplicates(configurations);
			configurations = sort(configurations, autoConfigurationMetadata);
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			checkExcludedClasses(configurations, exclusions);
			configurations.removeAll(exclusions);
			configurations = filter(configurations, autoConfigurationMetadata);
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return StringUtils.toStringArray(configurations);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}
}

selectImports()方法的实现,就是拿到META-INF/spring.factories文件中所有的xxxAutoConfiguration类的全限定类名,将他们注入到IoC容器中。

六. @Import源码分析

下面以@Import使用ImportSelector为例,分析导入过程的源码。

IoC容器启动时,调用AbstractApplicationContext的refresh()方法。该方法是Spring整个容器十分重要的一个方法,前面章节中对其进行过详细介绍,这里不再赘述。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

refresh()的处理中有一步是调用所有的BeanFactoryPostProcessor进行后处理,即invokeBeanFactoryPostProcessors()方法。BeanFactoryPostProcessor作用同BeanPostProcessor类似,区别在于BeanFactoryPostProcessor的处理是容器级别的,针对于BeanDefinition进行处理。查看invokeBeanFactoryPostProcessors()方法会调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()代理,该方法会扫描容器内部分所有BeanDefinitionRegistryPostProcessor,调用其postProcessBeanDefinitionRegistry()方法,进行BeanDefinition注册的后处理。

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口,主要功能是在注册BeanDefinition后进行一些后置处理,它的一个重要的实现类是ConfigurationClassPostProcessor,主要处理基于@Configuration配置的组件。ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法会调用processConfigBeanDefinitions()方法,处理基于配置的BeanDefinition,源码如下:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    //获取所有标记了@Configuration注解的类,封装成BeanDefinitionHolder集合
    List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    //如果没有@Configuraion,理解返回
    if (configCandidates.isEmpty()) {
        return;
    }

    //如果@Configuraion配置了@Order,则对其进行排序
    Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
        @Override
        public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
            int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
        }
    });

    SingletonBeanRegistry singletonRegistry = null;
    if (registry instanceof SingletonBeanRegistry) {
        singletonRegistry = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
            BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
        }
    }

    //解析@Configuration类
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
    do {
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<String>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
                        !alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());

    if (singletonRegistry != null) {
        if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
            singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
        }
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

该方法的主要功能就是扫描所有标记了@Configuration的类,将其封装成BeanDefinitionHolder集合,并对配置了@Order的元素进行排序。排序后,调用ConfigurationClassParser对象的parse()方法解析@Configuration类。

ConfigurationClassParser类的作用就是对@Configuration类进行解析,核心处理在doProcessConfigurationClass()方法中:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    processMemberClasses(configClass, sourceClass);

    // 解析@PropertySource注解
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                        "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    //解析@ComponentScan注解
    AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
    if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
                parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
            }
        }
    }

    //解析@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    //解析@ImportResource注解
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

   ...
}

解析@Import的注解在processImports()方法中:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

    if (importCandidates.isEmpty()) {
        return;
    }

    if (checkForCircularImports && this.importStack.contains(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                //处理基于ImportSelector方式的导入,调用selectImports()方法获取所有需要导入的类的全限定类名
                if (candidate.isAssignable(ImportSelector.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    invokeAwareMethods(selector);
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectors.add(
                            new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    else {
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                
                //处理基于ImportBeanDefinitionRegistrar方式的导入,调用registerBeanDefinitions()方法注册额外的BeanDefinition
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    invokeAwareMethods(registrar);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                
                else {
                    //处理普通Configuration或者Bean的导入
                    this.importStack.registerImport(
                        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
                                                   configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}