Spring Import 三种用法与源码解读

  最近在看Spring Cloud相关的源码,每次引入一个新的starter,发现都会加一些enable的注解,比如:@EnableDiscoveryClient,用于将应用注册到Eureka Server并将Eureka Server有的服务拉取到微服务系统。点开EnableDiscoveryClient源码,便会发现里面用到了@import注解。源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

    /**
     * If true, the ServiceRegistry will automatically register the local server.
     */
    boolean autoRegister() default true;
}

这个@import的作用是什么呢,它是如何工作的呢?我们在项目里如何应用@import导入我们自定义的类?

Import的用法

  Spring 3.0之前,我们的Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内。Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述。我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述。@Import注解提供了@Bean注解的功能,同时还有xml配置文件里<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration。先看看@Import注解的源码吧:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

//这里说了可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用噢 或者常用的(regular component classes )也就是Bean
    /**
     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
     * or regular component classes to import.
     */
    Class<?>[] value();

}

从源码里可以看出Import可以配合 Configuration , ImportSelector, ImportBeanDefinitionRegistrar 来使用,下面的or表示也可以把Import当成普通的Bean来使用,只是使用方式上有点区别,@Import只允许放到类上面,不能放到方法上。下面我们来看具体的使用方式。在使用之前我们有几个类在下面几种方式里都会用到,下面把这些公共的类放到一起。

  • 首先这里有个User的实体类,做为Bean里的参数,在实际的测试中也可以没有这个类,为了例子的完整性,我还是把代码贴到下面:
package com.ivan.entity;

public class User {

    private Integer id;
    
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}
  • 定义了一个UserService接口类,为了是体现Spring的面向接口编程。代码如下:
package com.ivan.service;

import com.ivan.entity.User;

public interface UserService {
    
    public int save(User user);
    
}
  • UserService接口对应的实现类,也是我们想注入到Spring IOC容器里的类,代码如下:
package com.ivan.service.impl;

import com.ivan.entity.User;
import com.ivan.service.UserService;

public class UserServiceImpl implements UserService {

    public int save(User user) {
        System.out.println("调用了当前方法");
        return 1;
    }

}
  • 测试类:
package com.ivan;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.ivan.config.Config;
import com.ivan.service.UserService;
import com.ivan.service.impl.UserServiceImpl;

/**
 * 
 * 功能描述: 
 * 
 * @version 2.0.0
 * @author zhiminchen
 */
public class App {
    
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);   
        UserService userService = (UserService)context.getBean(UserServiceImpl.class);
        userService.save(null);
        context.close();
    }
}

在测试类App里,用到了Config.class,不同的Import使用方式,这里的代码是不一样的,下面来看具体Import在使用上的区别:

1:最简单的导入类的方式

  这是最简单的一种将类加入到Spring IOC容器的方式,直接将类的class加到Import的value里,Config的代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.ivan.service.impl.UserServiceImpl;

@Configuration
@Import(value={UserServiceImpl.class})
public class Config {

}

启动测试类,发现调用到了我们的UserServiceImpl里的save方法,这样将类注入的方式有个问题就是没法注入参数。也就是说UserServiceImpl提供的应该是无参的构造方法。这种方式注入类在Spring内部用的并不多。最多的使用方式还是下面两种。

2.通过ImportBeanDefinitionRegistrar将类注入到Spring IOC容器

  Import注解通过配合ImportBeanDefinitionRegistrar类将类注入Spring IOC容器里。ImportBeanDefinitionRegistrar类的源码如下:

public interface ImportBeanDefinitionRegistrar {

    /**
     * Register bean definitions as necessary based on the given annotation metadata of
     * the importing {@code @Configuration} class.
     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
     * registered here, due to lifecycle constraints related to {@code @Configuration}
     * class processing.
     * @param importingClassMetadata annotation metadata of the importing class
     * @param registry current bean definition registry
     */
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

从上面的代码可以看出在注入Spring IOC容器的时候,我们肯定是通过registry这个变量了,而importingClassMetadata可以得到类的元数据信息。我们自定义的UserServiceBeanDefinitionRegistrar 类定义如下:

package com.ivan.bean.registrar;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import com.ivan.service.impl.UserServiceImpl;

public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);
        //通过registry就可以注入到容器里啦
        registry.registerBeanDefinition("userService", userService.getBeanDefinition());
    }

}

Config类的Import class需要改成UserServiceBeanDefinitionRegistrar.classs。代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.ivan.bean.registrar.UserServiceBeanDefinitionRegistrar;

@Configuration(value="ivan_test")
@Import(value={UserServiceBeanDefinitionRegistrar.class})
public class Config {

}

可以明显的看出通过ImportBeanDefinitionRegistrar的方式我们可以对类进行个性化的定制,比如对需要传入的参数进行修改,也可以通过ImportBeanDefinitionRegistrar注入一批相似的类。有BeanDefinitionRegistry对象也有可以控制Spring IOC容器里Bean的定义,想做些什么也就方便很多了。

3.通过ImportSelector方式注入Bean

上面通过ImportBeanDefinitionRegistrar的方式注入的实例需要我们操作BeanDefinitionRegistry 对象,而通过ImportSelector方式我们可以不操作BeanDefinitionRegistry 对象,只需要告诉容器我们需要注入类的完整类名就好。ImportSelector类的源码如下:

public interface ImportSelector {

    /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

上面的代码中可以看出需要返回一个String数组给到容器,传入的参数可以得到注解的元数据信息,现在我们来模拟下我们在Cloud代码里看到的enable都是如何通过ImportSelector方式来实现的。首先我们需要自定义一个注解,源码如下:

package com.ivan.enable;

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;

import org.springframework.context.annotation.Import;

import com.ivan.select.UserServiceImportSelect;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(UserServiceImportSelect.class)
public @interface EnableUserService {
    
    String name ();

}

EnableUserService 是个注解,里面通过@Import引入了UserServiceImportSelect,具体注入的逻辑在UserServiceImportSelect这个类里面,我们的注解同时定义了一个name属性,这里只是为了测试,在实际中你可以定义你需要的属性,然后在具体的ImportSelect里根据属性的值进行不同的配置。UserServiceImportSelect属性的代码如下:

package com.ivan.select;

import java.util.Map;
import java.util.Map.Entry;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import com.ivan.enable.EnableUserService;
import com.ivan.service.impl.UserServiceImpl;

public class UserServiceImportSelect implements ImportSelector{

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        
       Map<String , Object> map = importingClassMetadata.getAnnotationAttributes(EnableUserService.class.getName(), true);
       for(Entry<String, Object> entry : map.entrySet()){
           System.out.println("key is : " + entry.getKey() + " value is : " + entry.getValue());
       }
        
       return new String[]{UserServiceImpl.class.getName()};
    }

}

可以通过importingClassMetadata这个属性得到用了EnableUserService注解的元信息。同时返回了一个我们需要注入的类的名称,这样就可以注入到容器里啦,Config类的代码如下:

package com.ivan.config;

import org.springframework.context.annotation.Configuration;

import com.ivan.enable.EnableUserService;

@Configuration()
@EnableUserService(name="ivan_test")
public class Config {

}

通过上面三步我们就可以完成自定义enable注解并完成相应Bean的注入,顺带提一下通过ImportBeanDefinitionRegistrar的方式同样可以自定义注解然后注入我们需要的类。必竟ImportBeanDefinitionRegistrar这种方式从灵活性上来说应该是最全的。

源码解读

  通过上面几种方式的应用,应该可以掌握@Import注解的应用了。但Spring IOC内部是如何做到类的注入的呢,下面就来读一下Spring IOC内部类的源码。读源码如何找到源码的入口是关键,这里有个小技巧,通过IDE的Call Hierarchy调用链我们可以找到关键的代码,我们知道@Import这个注解无论它多么神,一定会调用到ImportSelector类里的selectImports方法的。我们通过这个方法就能找到如下的调用关系,如下图所示:

image.png

  在Spring IOC容器加载过程中会调用BeanFactoryPostProcessor方法,以处理如对BeanDefinition进行修改等操作。从调用链上看我们需要关注ConfigurationClassPostProcessor这个类,从名字中我们也可以看出他一定跟@Configuration这个注解有关。下面对ConfigurationClassPostProcessor的关键代码进行注释:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

         /**
           *在容器初使化的时候会调用到这个方法里,
          **/
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        int factoryId = System.identityHashCode(beanFactory);
        if (this.factoriesPostProcessed.contains(factoryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + beanFactory);
        }
        this.factoriesPostProcessed.add(factoryId);
        if (!this.registriesPostProcessed.contains(factoryId)) {
            // BeanDefinitionRegistryPostProcessor hook apparently not supported...
            // Simply call processConfigurationClasses lazily at this point then.
                        //Configuration肯定是在这个方法里处理的
            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
        }

        enhanceConfigurationClasses(beanFactory);
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }


    /** 这里的注释已经说了是处理Configuration的啦
     * Build and validate a configuration model based on the registry of
     * {@link Configuration} classes.
     */
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        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)) {

//这里会将类里有Configuration, Component, ComponentScan, Import, ImportResource 这些注解过的类加到configCandidates容器里
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
//如果没有上面提到的类,则直接返回
        // Return immediately if no @Configuration classes were found
        if (configCandidates.isEmpty()) {
            return;
        }

//对晒出来的类根据Order进行排序
        // Sort by previously determined @Order value, if applicable
        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;
            }
        });

        // Detect any custom bean name generation strategy supplied through the enclosing application context
        SingletonBeanRegistry sbr = null;
        if (registry instanceof SingletonBeanRegistry) {
            sbr = (SingletonBeanRegistry) registry;
            if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
                BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
                 //具体的解析是通过ConfigurationClassParser
        // Parse each @Configuration class
        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 {
//调用ConfigurationClassParser的parse方法进行解析
            parser.parse(candidates);
            parser.validate();

            Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);

            // Read the model and create bean definitions based on its content
            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 bd = registry.getBeanDefinition(candidateName);
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                            candidates.add(new BeanDefinitionHolder(bd, candidateName));
                        }
                    }
                }
                candidateNames = newCandidateNames;
            }
        }
        while (!candidates.isEmpty());

        // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
        if (sbr != null) {
            if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }
        }

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


}

上面的分析最终解析Import类是在ConfigurationClassParser这个类上面,从上面的调用图上我们也可以看出会调用到ConfigurationClassParser这个类上,下面对这个类的关键代码进行注释(代码只截取了部分源码,且没按源码里的顺序,只是为了阅读的方便):

class ConfigurationClassParser {

      /**
        *这是解析的入口方法,传入的参数是有特殊注解的Class类
      **/
    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
                      //无论bd属于下面的那三种情况,都会调用processConfigurationClass方法
            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);
            }
        }

        processDeferredImportSelectors();
    }

//下面三种方法用于处理不同的BeanDefinition 类型,但最终都会调用到processConfigurationClass方法
    protected final void parse(String className, String beanName) throws IOException {
        MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
        processConfigurationClass(new ConfigurationClass(reader, beanName));
    }

    protected final void parse(Class<?> clazz, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(clazz, beanName));
    }

    protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(metadata, beanName));
    }

//这里面处理Configuration 标注的class类
    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }

        ConfigurationClass existingClass = this.configurationClasses.get(configClass);
                //在这里处理Cconfiguration重复import
        if (existingClass != null) {
            if (configClass.isImported()) {
                if (existingClass.isImported()) {
                    existingClass.mergeImportedBy(configClass);
                }
                // Otherwise ignore new imported config class; existing non-imported class overrides it.
                return;
            }
            else {
                // Explicit bean definition found, probably replacing an import.
                // Let's remove the old one and go with the new one.
                this.configurationClasses.remove(configClass);
                for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
                    if (configClass.equals(it.next())) {
                        it.remove();
                    }
                }
            }
        }

        // Recursively process the configuration class and its superclass hierarchy.
                //这里通过循环处理上面的注解标注的类,得到SourceClass对象
        SourceClass sourceClass = asSourceClass(configClass);
        do {
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);

        this.configurationClasses.put(configClass, configClass);
    }

/**
**这个方法里会处理具体的各种注解类,@Configuration @Import等
*/
    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {

        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass);

        // Process any @PropertySource annotations
        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");
            }
        }

        // Process any @ComponentScan annotations
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() &&
                !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            for (AnnotationAttributes componentScan : componentScans) {
                // The config class is annotated with @ComponentScan -> perform the scan immediately
                Set<BeanDefinitionHolder> scannedBeanDefinitions =
                        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                // Check the set of scanned definitions for any further config classes and parse recursively if needed
                for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }
                 //在这个方法里处理Import注解,
        // Process any @Import annotations
        processImports(configClass, sourceClass, getImports(sourceClass), true);

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

        // Process individual @Bean methods
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        // Process default methods on interfaces
        processInterfaces(configClass, sourceClass);

        // Process superclass, if any
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                // Superclass found, return its annotation metadata and recurse
                return sourceClass.getSuperClass();
            }
        }

        // No superclass -> processing is complete
        return null;
    }

//在这里处理具体的Import
    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, 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) {
//这里看到了熟悉的ImportSelector了吧
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to it to determine imports
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, this.registry);
                        if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
//这里会把DeferredImportSelector 加入到deferredImportSelectors集合类中,在parse方法里调用接口定义的方法
                            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);
                        }
                    }
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
//在这里处理ImportBeanDefinitionRegistrar,
                        // Candidate class is an ImportBeanDefinitionRegistrar ->
                        // delegate to it to register additional bean definitions
                        Class<?> candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                registrar, this.environment, this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {
//将import当成Configuration来使用也是我们最简单的一种应用的方式
                        // 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));
                    }
                }
            }
            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();
            }
        }
    }

}

上面整体上分析了一下Spring里是如何处理@Import这个注解的,只跟踪了处理ImportSelector这种方式的处理逻辑,代码里都有比较详细的注释。其它两种方式分析的方法大同小异。从整体上来理解的话大概就下面几个流程:

  • Spring IOC容器初使化的时候会调用AbstractApplicationContext 的refresh方法
  • 在refresh里会调用各种BeanFactoryPostProcessor, 其中就包括我们需要关注的ConfigurationClassPostProcessor。
  • ConfigurationClassPostProcessor 不但用于处理@Configuration注解,里面也有处理@Import注解。
  • 最终的处理是通过 ConfigurationClassParser 这个类完成对Import各种情况的处理
  • Import有三种导入方式,从代码里我们可以看到对于不同方式的处理。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CodingBlock

小白也能看懂的插件化DroidPlugin原理(三)-- 如何拦截startActivity方法

前言:在前两篇文章中分别介绍了动态代理、反射机制和Hook机制,如果对这些还不太了解的童鞋建议先去参考一下前两篇文章。经过了前面两篇文章的铺垫,终于可以玩点真...

2796
来自专栏JackieZheng

Spring Boot系列——Spring Boot如何启动

​上篇《Spring Boot系列——5分钟构建一个应用》介绍了如何快速创建一个Spring Boot项目并运行。虽然步骤少流程简单,为开发者省去了很多重复性的...

883
来自专栏javathings

@SpringBootApplication 的作用是什么

@SpringBootApplication 标注的类为 Spring Boot 的主配置类,Spring Boot 会运行这个类的 main 方法来启动 Sp...

7872
来自专栏清晨我上码

spring mvc 国际化的几种方案

通过设置浏览器请求测试:http://localhost:8080/xxx/nation/test

6043
来自专栏石奈子的Java之路

原 荐 SpringBoot 2.0 系列0

3763
来自专栏Hongten

apache的开源项目-模板引擎(Velocity)_学习了两天就上手啦_源码下载

首先,如果你对Velocity不是很了解,还是建议你去apache的官方网站上去走走....

1131
来自专栏F_Alex

(四)SpringBoot2.0基础篇- 多数据源,JdbcTemplate和JpaRepository

3554
来自专栏java、Spring、技术分享

Feign与Spring Cloud源码解析

  在Feign的官方文档上, 我们可以看到Feign最重要的一句话是:Feign makes writing java http clients easier...

4802
来自专栏后端之路

基于SpringBoot的CodeGenerator

背景 目前组织上对于一个基础的crud的框架需求较多 因此选择了SpringBoot作为基础选型。 Spring Boot是由Pivotal团队提供的全新框架,...

1.8K11
来自专栏流柯技术学院

CentOS升级Python2.7导致使用pip等命令安装模块失败

出现这个问题是因为:虽然已经把Python升级到了2.7版本,但是pip仍然是原来的版本,仍在原来python的site-package里面

1413

扫码关注云+社区