Spring Import 三种用法与源码解读

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

  最近在看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配置文件里标签组织多个分散的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方法的。我们通过这个方法就能找到如下的调用关系,如下图所示:

20191102100725\_1.png 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有三种导入方式,从代码里我们可以看到对于不同方式的处理。

来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Import 三种用法与源码解读

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏