Spring中@Import 用法

@Import 的作用: @Import用来导入@Configuration注解的配置类、注入普通类、导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类。源码如下:

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;

/**
 * Indicates one or more <em>component classes</em> to import &mdash; typically
 * {@link Configuration @Configuration} classes.
 *
 * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
 * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
 * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
 *
 * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
 * injection. Either the bean itself can be autowired, or the configuration class instance
 * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
 * navigation between {@code @Configuration} class methods.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportBeanDefinitionRegistrar
 * @see ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
     * {@link Configuration @Configuration}, {@link ImportSelector},
     * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();

}

由Feign的开启注解@EnableFeignClients引出的Import的使用方法。

开启Feign的注解如下:

package org.springframework.cloud.openfeign;

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;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

核心是引入了一个FeignClientsRegistrar 类。也就是这个类做了手脚。

1. Import 实践

1. Springboot主启动类: 所在的包是cn.qz 所以默认只能扫描本包及子包

package cn.qz;

import cn.qz.user.UserDao;
import cn.zz.BeanConfiguration;
import cn.zz.DeptDao;
import cn.zz.DocumentImportBeanDefinitionRegistrar;
import cn.zz.MsgBeanImportSelector;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@ComponentScan
@Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class})
//@EnableAspectJAutoProxy
public class App {
    public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
//        System.setProperty("cglib.debugLocation", "F:/proxy");

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
        UserDao bean = applicationContext.getBean(UserDao.class);
        bean.test1();
    }
}

2. BeanConfiguration 配置类

package cn.zz;

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

@Configuration
public class BeanConfiguration {

    @Bean
    public GroupDao groupDao() {
        return new GroupDao();
    }

}

GroupDao

package cn.zz;


public class GroupDao {

    public GroupDao() {
        System.out.println("GroupDao");
    }
}

3. DeptDao

package cn.zz;

import org.springframework.stereotype.Component;

@Component
public class DeptDao {

    public DeptDao() {
        System.out.println("DeptDao*****");
    }

    public void test1() {
        System.out.println("UserDao#test1");
    }
}

4. MsgBeanImportSelector

package cn.zz;

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

public class MsgBeanImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Msg.class.getName()};
    }
}

Msg 

package cn.zz;

public class Msg {

    public Msg() {
        System.out.println("msg");
    }
}

5. DocumentImportBeanDefinitionRegistrar

package cn.zz;

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

public class DocumentImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registerSyntheticBeanIfMissing(registry, "document",
                Document.class);
    }

    // 检查并注册Bean
    private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
        // 检查指定类型的Bean name数组是否存在,如果不存在则创建Bean并注入到容器中
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
        beanDefinition.setSynthetic(true);

        // 注册Bean
        registry.registerBeanDefinition(name, beanDefinition);
    }
}

Document

package cn.zz;

public class Document {

    public Document() {
        System.out.println("Document");
    }
}

启动App主类查看控制台:

DeptDao*****
GroupDao
msg
Document
UserDao#test1

2. 机制理解

 在之前IoC学习中,了解到配置方式注册BeanDefinition的入口是:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList();
        String[] candidateNames = registry.getBeanDefinitionNames();
        String[] var4 = candidateNames;
        int var5 = candidateNames.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String beanName = var4[var6];
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
                if (this.logger.isDebugEnabled()) {
                    this.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));
            }
        }

        if (!configCandidates.isEmpty()) {
            configCandidates.sort((bd1, bd2) -> {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return Integer.compare(i1, i2);
            });
            SingletonBeanRegistry sbr = null;
            if (registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry)registry;
                if (!this.localBeanNameGeneratorSet) {
                    BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
                    if (generator != null) {
                        this.componentScanBeanNameGenerator = generator;
                        this.importBeanNameGenerator = generator;
                    }
                }
            }

            if (this.environment == null) {
                this.environment = new StandardEnvironment();
            }

            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
                parser.parse(candidates);
                parser.validate();
                Set<ConfigurationClass> configClasses = new LinkedHashSet(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(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet();
                    Iterator var12 = alreadyParsed.iterator();

                    while(var12.hasNext()) {
                        ConfigurationClass configurationClass = (ConfigurationClass)var12.next();
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }

                    String[] var23 = newCandidateNames;
                    int var24 = newCandidateNames.length;

                    for(int var14 = 0; var14 < var24; ++var14) {
                        String candidateName = var23[var14];
                        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());

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

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

        }
    }

1.  创建parser以及解析

            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
                parser.parse(candidates);
                parser.validate();
    ...

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) 解析如下:

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        Iterator var2 = configCandidates.iterator();

        while(var2.hasNext()) {
            BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
            BeanDefinition bd = holder.getBeanDefinition();

            try {
                if (bd instanceof AnnotatedBeanDefinition) {
                    this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
                } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
                    this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
                } else {
                    this.parse(bd.getBeanClassName(), holder.getBeanName());
                }
            } catch (BeanDefinitionStoreException var6) {
                throw var6;
            } catch (Throwable var7) {
                throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);
            }
        }

        this.deferredImportSelectorHandler.process();
    }

接着调用:

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

    protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
        if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }

                    return;
                }

                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }

            ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);

            do {
                sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
            } while(sourceClass != null);

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

接下来就是到org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 处理配置信息,Import类也是在这里处理,如下:

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

        if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
            // 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.info("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());
                    }
                }
            }
        }

        // Process any @Import annotations
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        // Process any @ImportResource annotations
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            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 != null && !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;
    }

  接下来从processImports(configClass, sourceClass, getImports(sourceClass), true); 分析

1. processImports(configClass, sourceClass, getImports(sourceClass), true); 开始处理Import 信息

(1) 首先是getImports 获取所有的配置类:

org.springframework.context.annotation.ConfigurationClassParser#getImports

    private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
        Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
        Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
        this.collectImports(sourceClass, imports, visited);
        return imports;
    }

    private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
        if (visited.add(sourceClass)) {
            Iterator var4 = sourceClass.getAnnotations().iterator();

            while(var4.hasNext()) {
                ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
                String annName = annotation.getMetadata().getClassName();
                if (!annName.equals(Import.class.getName())) {
                    this.collectImports(annotation, imports, visited);
                }
            }

            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }

    }

  可以看到是递归获取类上注解信息,如果是Import 则获取其value属性,然后返回去。

(2) 经过这一步获取到的Set<ConfigurationClassParser.SourceClass> 信息如下:

 2. org.springframework.context.annotation.ConfigurationClassParser#processImports 方法开始处理@Import 上面引入的类,如下:

    private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
        if (!importCandidates.isEmpty()) {
            if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
                this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
            } else {
                this.importStack.push(configClass);

                try {
                    Iterator var6 = importCandidates.iterator();

                    while(var6.hasNext()) {
                        ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
                        Class candidateClass;
                        if (candidate.isAssignable(ImportSelector.class)) {
                            candidateClass = candidate.loadClass();
                            ImportSelector selector = (ImportSelector)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<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
                                this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                            }
                        } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                            candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        } else {
                            this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                        }
                    }
                } catch (BeanDefinitionStoreException var17) {
                    throw var17;
                } catch (Throwable var18) {
                    throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
                } finally {
                    this.importStack.pop();
                }
            }

        }
    }

可以看到:

如果不是ImportSelector, 也不是ImportBeanDefinitionRegistrar, 会默认作为@Configuration 来处理。

如果是ImportSelector: 先反射创建对象,然后调用其Aware 接口。 然后调用selectImports 返回的类作为配置类再次递归收集配置类

如果是ImportBeanDefinitionRegistrar 类, 先反射创建对象,然后反射Aware 接口,然后把返回的对象缓存起来。

org.springframework.context.annotation.ParserStrategyUtils#invokeAwareMethods  调用的相关接口如下:

    public static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
            ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {

        if (parserStrategyBean instanceof Aware) {
            if (parserStrategyBean instanceof BeanClassLoaderAware) {
                ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
                        ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
                if (classLoader != null) {
                    ((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
                }
            }
            if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
                ((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
            }
            if (parserStrategyBean instanceof EnvironmentAware) {
                ((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
            }
            if (parserStrategyBean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
            }
        }
    }

 

@Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class}) 注解如上。

1》先处理 DeptDao.class,走的方法是下面代码:(也就是最后的else里面的代码)

                            this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);

org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

    protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
        if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }

                    return;
                }

                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }

            ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);

            do {
                sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
            } while(sourceClass != null);

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

(1)do 方法块来调用org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

    @Nullable
    protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
        if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
            this.processMemberClasses(configClass, sourceClass, filter);
        }

        Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();

        AnnotationAttributes importResource;
        while(var4.hasNext()) {
            importResource = (AnnotationAttributes)var4.next();
            if (this.environment instanceof ConfigurableEnvironment) {
                this.processPropertySource(importResource);
            } else {
                this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }

        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            Iterator var14 = componentScans.iterator();

            while(var14.hasNext()) {
                AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();
                Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                Iterator var8 = scannedBeanDefinitions.iterator();

                while(var8.hasNext()) {
                    BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }

                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        this.parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

        this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
        importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            String[] resources = importResource.getStringArray("locations");
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            String[] var20 = resources;
            int var22 = resources.length;

            for(int var23 = 0; var23 < var22; ++var23) {
                String resource = var20[var23];
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

        Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
        Iterator var18 = beanMethods.iterator();

        while(var18.hasNext()) {
            MethodMetadata methodMetadata = (MethodMetadata)var18.next();
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        this.processInterfaces(configClass, sourceClass);
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                return sourceClass.getSuperClass();
            }
        }

        return null;
    }

(2) 然后this.configurationClasses.put(configClass, configClass); 添加到配置类中

2》 接下来处理BeanConfiguration,这个也是走最后的else代码块,和上面不同的是调用org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata 会获取到@Bean 的方法,然后调用下面代码加到缓存中:

        while(var18.hasNext()) {
            MethodMetadata methodMetadata = (MethodMetadata)var18.next();
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

最后调用下面方法将自己加到缓存中:

this.configurationClasses.put(configClass, configClass);

3》接下来处理MsgBeanImportSelector , 走的candidate.isAssignable(ImportSelector.class) 的代码块

然后走下面代码进行处理,也是加到缓存中

 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
 Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
 this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);

4》接下来处理DocumentImportBeanDefinitionRegistrar:

(1)走candidate.isAssignable(ImportBeanDefinitionRegistrar.class) 代码块:

(2) org.springframework.context.annotation.ConfigurationClass#addImportBeanDefinitionRegistrar 添加到缓存中

    public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
        this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
    }
    private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap();

 

  可以看到上面有个递归调用的过程,如果是普通类和Configuration类会作为配置类,递归做处理,这个稍后验证。

 

5》 上述流程走完之后加的缓存信息如下:

org.springframework.context.annotation.ConfigurationClassParser#configurationClasses

 

 2. 利用上面的缓存开始生成注册BeanDefinition

                Set<ConfigurationClass> configClasses = new LinkedHashSet(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);

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions如下:

    public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
        ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator = new ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator();
        Iterator var3 = configurationModel.iterator();

        while(var3.hasNext()) {
            ConfigurationClass configClass = (ConfigurationClass)var3.next();
            this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
        }

    }

也就是上面的五个:

 接下来就是生成BeanDefinition信息,org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass:

    private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator) {
        if (trackedConditionEvaluator.shouldSkip(configClass)) {
            String beanName = configClass.getBeanName();
            if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
                this.registry.removeBeanDefinition(beanName);
            }

            this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        } else {
            if (configClass.isImported()) {
                this.registerBeanDefinitionForImportedConfigurationClass(configClass);
            }

            Iterator var3 = configClass.getBeanMethods().iterator();

            while(var3.hasNext()) {
                BeanMethod beanMethod = (BeanMethod)var3.next();
                this.loadBeanDefinitionsForBeanMethod(beanMethod);
            }

            this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
            this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
        }
    }

分析org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 用@Bean 注册的Bean信息

    private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
        ConfigurationClass configClass = beanMethod.getConfigurationClass();
        MethodMetadata metadata = beanMethod.getMetadata();
        String methodName = metadata.getMethodName();
        if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
            configClass.skippedBeanMethods.add(methodName);
        } else if (!configClass.skippedBeanMethods.contains(methodName)) {
            AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
            Assert.state(bean != null, "No @Bean annotation attributes");
            List<String> names = new ArrayList(Arrays.asList(bean.getStringArray("name")));
            String beanName = !names.isEmpty() ? (String)names.remove(0) : methodName;
            Iterator var8 = names.iterator();

            while(var8.hasNext()) {
                String alias = (String)var8.next();
                this.registry.registerAlias(beanName, alias);
            }

            if (this.isOverriddenByExistingDefinition(beanMethod, beanName)) {
                if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
                    throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(), beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() + "' clashes with bean name for containing configuration class; please make those names unique!");
                }
            } else {
                ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition(configClass, metadata);
                beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
                if (metadata.isStatic()) {
                    if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
                        beanDef.setBeanClass(((StandardAnnotationMetadata)configClass.getMetadata()).getIntrospectedClass());
                    } else {
                        beanDef.setBeanClassName(configClass.getMetadata().getClassName());
                    }

                    beanDef.setUniqueFactoryMethodName(methodName);
                } else {
                    beanDef.setFactoryBeanName(configClass.getBeanName());
                    beanDef.setUniqueFactoryMethodName(methodName);
                }

                if (metadata instanceof StandardMethodMetadata) {
                    beanDef.setResolvedFactoryMethod(((StandardMethodMetadata)metadata).getIntrospectedMethod());
                }

                beanDef.setAutowireMode(3);
                beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
                AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
                Autowire autowire = (Autowire)bean.getEnum("autowire");
                if (autowire.isAutowire()) {
                    beanDef.setAutowireMode(autowire.value());
                }

                boolean autowireCandidate = bean.getBoolean("autowireCandidate");
                if (!autowireCandidate) {
                    beanDef.setAutowireCandidate(false);
                }

                String initMethodName = bean.getString("initMethod");
                if (StringUtils.hasText(initMethodName)) {
                    beanDef.setInitMethodName(initMethodName);
                }

                String destroyMethodName = bean.getString("destroyMethod");
                beanDef.setDestroyMethodName(destroyMethodName);
                ScopedProxyMode proxyMode = ScopedProxyMode.NO;
                AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
                if (attributes != null) {
                    beanDef.setScope(attributes.getString("value"));
                    proxyMode = (ScopedProxyMode)attributes.getEnum("proxyMode");
                    if (proxyMode == ScopedProxyMode.DEFAULT) {
                        proxyMode = ScopedProxyMode.NO;
                    }
                }

                BeanDefinition beanDefToRegister = beanDef;
                if (proxyMode != ScopedProxyMode.NO) {
                    BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
                    beanDefToRegister = new ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition((RootBeanDefinition)proxyDef.getBeanDefinition(), configClass, metadata);
                }

                if (logger.isTraceEnabled()) {
                    logger.trace(String.format("Registering bean definition for @Bean method %s.%s()", configClass.getMetadata().getClassName(), beanName));
                }

                this.registry.registerBeanDefinition(beanName, beanDefToRegister);
            }
        }
    }

信息如下:

 

   这里看到了用方法名称作为BeanNAME,然后注入到IoC容器。

 

最后两行代码如下:

        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

  第一行是导入@ImportResource 引入的资源文件,这个注解是为了导入 xml 配置的bean, 也就是兼容原来的xml 配置的方式。

  第二行代码就是递归遍历ImportBeanDefinitionRegistrar, 调用其registerBeanDefinitions 方法:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars 手动进行注入

    private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
        registrars.forEach((registrar, metadata) ->
                registrar.registerBeanDefinitions(metadata, this.registry));
    }

 

 

补充:验证@Import 的普通类不用加@Component 注解,以及@Import 引入的普通类是作为配置类被递归处理的,可以注册Bean 

App:

package cn.qz;

import cn.qz.user.UserDao;
import cn.zz.BeanConfiguration;
import cn.zz.DeptDao;
import cn.zz.DocumentImportBeanDefinitionRegistrar;
import cn.zz.MsgBeanImportSelector;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@ComponentScan
@Import({DeptDao.class, BeanConfiguration.class, MsgBeanImportSelector.class, DocumentImportBeanDefinitionRegistrar.class})
//@EnableAspectJAutoProxy
public class App {
    public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
//        System.setProperty("cglib.debugLocation", "F:/proxy");

        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
        UserDao bean = applicationContext.getBean(UserDao.class);
        bean.test1();
    }
}

Dept:

package cn.zz;

import org.springframework.context.annotation.Bean;

public class DeptDao {

    public DeptDao() {
        System.out.println("DeptDao*****");
    }

    public void test1() {
        System.out.println("UserDao#test1");
    }

    @Bean
    public PackageDao packageDao() {
        return new PackageDao();
    }
}

Package:

package cn.zz;

public class PackageDao {

    public PackageDao() {
        System.out.println("PackageDao");
    }
}

结果:

DeptDao*****
PackageDao
GroupDao
msg
Document
UserDao#test1

补充: 普通类作为配置类使用是因为递归调用了调用org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass 方法来处理,这个里面也处理了@Import,因此@Import 也可以被传递调用

Dept:

package cn.zz;

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

@Import(Product.class)
public class DeptDao {

    public DeptDao() {
        System.out.println("DeptDao*****");
    }

    public void test1() {
        System.out.println("UserDao#test1");
    }

    @Bean
    public PackageDao packageDao() {
        return new PackageDao();
    }
}

Product:

package cn.zz;


public class Product {

    public Product() {
        System.out.println("product+++");
    }
}

结果:

product+++
DeptDao*****
PackageDao
GroupDao
msg
Document
UserDao#test1

 

  总结:@Import用来导入@Configuration注解的配置类、注入普通类、导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类。同时由于处理的时候是递归处理,因此在@Import 引入的类又可以@Import 其他类。@Import 导入的类可以不写@Component注解或者其他注册注解,默认会作为配置类(Configuration)处理,这样在不写Configuration的前提下可以用@Bean 注入Bean信息。

 

posted @ 2021-03-05 23:59  QiaoZhi  阅读(846)  评论(0编辑  收藏  举报