使用@import导入实现了ImportBeanDefinitionRegistrar接口的类,不能被注册为bean

今天在调试公司spring项目的时候发现了这样一个问题,由于我们的项目使用的是springboot就以springboot为例,代码如下:

复制代码
@Import({DataSourceRegister.class,A.class})
@SpringBootApplication
@ComponentScan("com.viewhigh.bi")
//@EnableCaching
public class BiApplication {
    public static void main(String[] args) {
        LogUtil.setEnabled(true);//开启日志输出

        SpringApplication sa = new SpringApplication(BiApplication.class);
        sa.setBannerMode(Banner.Mode.LOG);
        sa.run(args);
    }
}
复制代码

springboot启动的时候,loder模块会根据“清单文件”加载该BIApplication类,并反射调用psvm入口函数main,但是一个很有意思的问题出现了,项目正常运行之后,在springcontext中可以找到Bean类A,但是无法找到DataSourceRegister这个类;

我们知道在spring4.2以后@Import注解也可以导入一个常规类,并将其注册为bean,那么为什么DataSourceRegister没有被注册为Bean呢?

DataSourceRegister类是用来进行初始化数据源和并提供了执行动态切换数据源的工具类

复制代码
public class DataSourceRegister<T> implements EnvironmentAware, ImportBeanDefinitionRegistrar {
    private javax.sql.DataSource defaultTargetDataSource;
    static final String MAINDATASOURCE = "mainDataSource";
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setEnvironment(Environment environment) {
    DruidEntity druidEntity </span>= FileUtil.readYmlByClassPath("db_info", DruidEntity.<span style="color: #0000ff;">class</span><span style="color: #000000;">);

    defaultTargetDataSource </span>=<span style="color: #000000;"> DataSourceUtil.createMainDataSource(druidEntity);
}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 0.将主数据源添加到数据源集合中</span>

DataSourceSet.putTargetDataSourcesMap(MAINDATASOURCE, defaultTargetDataSource);
//1.创建DataSourceBean
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DataSource.
class);
beanDefinition.setSynthetic(
true);
MutablePropertyValues mpv
= beanDefinition.getPropertyValues();
//spring名称约定为defaultTargetDataSource和targetDataSources
mpv.addPropertyValue("defaultTargetDataSource", defaultTargetDataSource);
mpv.addPropertyValue(
"targetDataSources", DataSourceSet.getTargetDataSourcesMap());
beanDefinitionRegistry.registerBeanDefinition(
"dataSource", beanDefinition);
}
}

复制代码

 看到代码后相信大家已经明白了,这个动态数据注册类实现了ImportBeanDefinitionRegistrar 接口,没错就是这个原因,由于实现了该接口让该类成为了拥有注册bean的能力。从原理上也能说得通作为一个Bean的注册类是没有必要和A类一样都被注册为Bean的!

 虽然这样解释也不为过但我仍然想一探究竟,本来想大概找找spring涉及关键类如:ConfigurationClass,ConfigurationClassParser等,可能由于不熟悉看到类中的代码就呵呵了,似乎无从下手!

 所以准备调试下spring启动部分的代码 ,这样会更清晰些!(以spring boot V1.5.6为例)

 ======

 spring boot启动时使用了SpringApplication类的run方法来牵引整个spring的初始化过程!!!

 没错了就是从run开始吧!

 代码如下:

复制代码
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        ApplicationArguments applicationArguments </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> DefaultApplicationArguments(args);
        ConfigurableEnvironment environment </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.prepareEnvironment(listeners, applicationArguments);
        Banner printedBanner </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.printBanner(environment);
        context </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.createApplicationContext();
        </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> FailureAnalyzers(context);
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.refreshContext(context);
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.afterRefresh(context, applicationArguments);
        listeners.finished(context, (Throwable)</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);
        stopWatch.stop();
        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">this</span><span style="color: #000000;">.logStartupInfo) {
            (</span><span style="color: #0000ff;">new</span> StartupInfoLogger(<span style="color: #0000ff;">this</span>.mainApplicationClass)).logStarted(<span style="color: #0000ff;">this</span><span style="color: #000000;">.getApplicationLog(), stopWatch);
        }

        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> context;
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Throwable var9) {
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
        </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> IllegalStateException(var9);
    }
}</span></pre>
复制代码

手一懒,就把整个方法直接copy了。这就是springboot在启动时的完整足迹。。。闲话少说我们直击关键点;

context = this.createApplicationContext();
this.refreshContext(context);

这两段是最关键的地方,阅读过一些spring书籍的兄弟都知道refreshContext就是在做spring运行后的初始化工作。那么在createApplicationContext当中,由于我们是web项目,则spring默认给我们创建了一个AnnotationConfigEmbeddedWebApplicationContext

当然它也是继承GenericWebApplicationContext类和GenericApplicationContext类的,那么他默认会持有一个DefaultListableBeanFactory对象,这个对象可以用来创建Bean,吼吼,这个块说的似乎没有意义哈!!!

接着往下走,进入refreshContext中会调用一系列的refresh方法,最终进入AbstractApplicationContext中:

复制代码
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Tell the subclass to refresh the internal bean factory.</span>
        ConfigurableListableBeanFactory beanFactory =<span style="color: #000000;"> obtainFreshBeanFactory();

        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Prepare the bean factory for use in this context.</span>

prepareBeanFactory(beanFactory);

        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Allows post-processing of the bean factory in context subclasses.</span>

postProcessBeanFactory(beanFactory);

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Invoke factory processors registered as beans in the context.</span>

invokeBeanFactoryPostProcessors(beanFactory);

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Register bean processors that intercept bean creation.</span>

registerBeanPostProcessors(beanFactory);

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Initialize message source for this context.</span>

initMessageSource();

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Initialize event multicaster for this context.</span>

initApplicationEventMulticaster();

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Initialize other special beans in specific context subclasses.</span>

onRefresh();

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Check for listener beans and register them.</span>

registerListeners();

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Instantiate all remaining (non-lazy-init) singletons.</span>

finishBeanFactoryInitialization(beanFactory);

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Last step: publish corresponding event.</span>

finishRefresh();
}

        </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (BeansException ex) {
            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (logger.isWarnEnabled()) {
                logger.warn(</span>"Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " +<span style="color: #000000;"> ex);
            }

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Destroy already created singletons to avoid dangling resources.</span>

destroyBeans();

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Reset 'active' flag.</span>

cancelRefresh(ex);

            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Propagate exception to caller.</span>
            <span style="color: #0000ff;">throw</span><span style="color: #000000;"> ex;
        }

        </span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Reset common introspection caches in Spring's core, since we
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> might not ever need metadata for singleton beans anymore...</span>

resetCommonCaches();
}
}
}

复制代码

 invokeBeanFactoryPostProcessors()方法就是Bean在注册前期做的一系列数据收集工作!

 跟着堆栈继续深入,会进入到这个方法中,这个方法就是初始化bean前的所有轨迹:

在invokeBeanFactoryPostProcessors方法中继续跟进一系列方法就会看到在一开始的时候spring会初始化几个系统固有的Bean:

继续调试后的关键点出现在这个方法中:

复制代码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
        String[] candidateNames = registry.getBeanDefinitionNames();
    </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String beanName : candidateNames) {
        BeanDefinition beanDef </span>=<span style="color: #000000;"> registry.getBeanDefinition(beanName);
        </span><span style="color: #0000ff;">if</span> (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||<span style="color: #000000;">
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (logger.isDebugEnabled()) {
                logger.debug(</span>"Bean definition has already been processed as a configuration class: " +<span style="color: #000000;"> beanDef);
            }
        }
        </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, <span style="color: #0000ff;">this</span><span style="color: #000000;">.metadataReaderFactory)) {
            configCandidates.add(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> BeanDefinitionHolder(beanDef, beanName));
        }
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Return immediately if no @Configuration classes were found</span>
    <span style="color: #0000ff;">if</span><span style="color: #000000;"> (configCandidates.isEmpty()) {
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Sort by previously determined @Order value, if applicable</span>
    Collections.sort(configCandidates, <span style="color: #0000ff;">new</span> Comparator&lt;BeanDefinitionHolder&gt;<span style="color: #000000;">() {
        @Override
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
            </span><span style="color: #0000ff;">int</span> i1 =<span style="color: #000000;"> ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            </span><span style="color: #0000ff;">int</span> i2 =<span style="color: #000000;"> ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            </span><span style="color: #0000ff;">return</span> (i1 &lt; i2) ? -1 : (i1 &gt; i2) ? 1 : 0<span style="color: #000000;">;
        }
    });

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Detect any custom bean name generation strategy supplied through the enclosing application context</span>
    SingletonBeanRegistry sbr = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">if</span> (registry <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> SingletonBeanRegistry) {
        sbr </span>=<span style="color: #000000;"> (SingletonBeanRegistry) registry;
        </span><span style="color: #0000ff;">if</span> (!<span style="color: #0000ff;">this</span>.localBeanNameGeneratorSet &amp;&amp;<span style="color: #000000;"> sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
            BeanNameGenerator generator </span>=<span style="color: #000000;"> (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            </span><span style="color: #0000ff;">this</span>.componentScanBeanNameGenerator =<span style="color: #000000;"> generator;
            </span><span style="color: #0000ff;">this</span>.importBeanNameGenerator =<span style="color: #000000;"> generator;
        }
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Parse each @Configuration class</span>
    ConfigurationClassParser parser = <span style="color: #0000ff;">new</span><span style="color: #000000;"> ConfigurationClassParser(
            </span><span style="color: #0000ff;">this</span>.metadataReaderFactory, <span style="color: #0000ff;">this</span>.problemReporter, <span style="color: #0000ff;">this</span><span style="color: #000000;">.environment,
            </span><span style="color: #0000ff;">this</span>.resourceLoader, <span style="color: #0000ff;">this</span><span style="color: #000000;">.componentScanBeanNameGenerator, registry);

    Set</span>&lt;BeanDefinitionHolder&gt; candidates = <span style="color: #0000ff;">new</span> LinkedHashSet&lt;BeanDefinitionHolder&gt;<span style="color: #000000;">(configCandidates);
    Set</span>&lt;ConfigurationClass&gt; alreadyParsed = <span style="color: #0000ff;">new</span> HashSet&lt;ConfigurationClass&gt;<span style="color: #000000;">(configCandidates.size());
    </span><span style="color: #0000ff;">do</span><span style="color: #000000;"> {
        parser.parse(candidates);
        parser.validate();

        Set</span>&lt;ConfigurationClass&gt; configClasses = <span style="color: #0000ff;">new</span> LinkedHashSet&lt;ConfigurationClass&gt;<span style="color: #000000;">(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        </span><span style="color: #008000;">//</span><span style="color: #008000;"> Read the model and create bean definitions based on its content</span>
        <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.reader == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
            </span><span style="color: #0000ff;">this</span>.reader = <span style="color: #0000ff;">new</span><span style="color: #000000;"> ConfigurationClassBeanDefinitionReader(
                    registry, </span><span style="color: #0000ff;">this</span>.sourceExtractor, <span style="color: #0000ff;">this</span>.resourceLoader, <span style="color: #0000ff;">this</span><span style="color: #000000;">.environment,
                    </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.importBeanNameGenerator, parser.getImportRegistry());
        }
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        </span><span style="color: #0000ff;">if</span> (registry.getBeanDefinitionCount() &gt;<span style="color: #000000;"> candidateNames.length) {
            String[] newCandidateNames </span>=<span style="color: #000000;"> registry.getBeanDefinitionNames();
            Set</span>&lt;String&gt; oldCandidateNames = <span style="color: #0000ff;">new</span> HashSet&lt;String&gt;<span style="color: #000000;">(Arrays.asList(candidateNames));
            Set</span>&lt;String&gt; alreadyParsedClasses = <span style="color: #0000ff;">new</span> HashSet&lt;String&gt;<span style="color: #000000;">();
            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (String candidateName : newCandidateNames) {
                </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd </span>=<span style="color: #000000;"> registry.getBeanDefinition(candidateName);
                    </span><span style="color: #0000ff;">if</span> (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, <span style="color: #0000ff;">this</span>.metadataReaderFactory) &amp;&amp;
                            !<span style="color: #000000;">alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames </span>=<span style="color: #000000;"> newCandidateNames;
        }
    }
    </span><span style="color: #0000ff;">while</span> (!<span style="color: #000000;">candidates.isEmpty());

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes</span>
    <span style="color: #0000ff;">if</span> (sbr != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
        </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
            sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
        }
    }

    </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.metadataReaderFactory <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.metadataReaderFactory).clearCache();
    }
}</span></pre>
复制代码

而通过不断重复调试确定获得注册Bean的列表应该发生在配置的“剖析阶段”,也就是parser.parse(candidates);这个方法的内部,到了这里基本问题的答案已经要浮出水面了,我也不再粘贴无用的代码,如果你真的对这个问题比骄傲好奇可以自己跟踪并练习调试的源码技巧!

当然在ConfigurationClassParser这个类中parse方法也是不少,只要静下心来逐渐分析,马上就能准确的找到Override的parse方法。。。

现在直接贴出最最关键的代码:

复制代码
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }
    ConfigurationClass existingClass </span>= <span style="color: #0000ff;">this</span><span style="color: #000000;">.configurationClasses.get(configClass);
    </span><span style="color: #0000ff;">if</span> (existingClass != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (configClass.isImported()) {
            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Otherwise ignore new imported config class; existing non-imported class overrides it.</span>
            <span style="color: #0000ff;">return</span><span style="color: #000000;">;
        }
        </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Explicit bean definition found, probably replacing an import.
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Let's remove the old one and go with the new one.</span>
            <span style="color: #0000ff;">this</span><span style="color: #000000;">.configurationClasses.remove(configClass);
            </span><span style="color: #0000ff;">for</span> (Iterator&lt;ConfigurationClass&gt; it = <span style="color: #0000ff;">this</span><span style="color: #000000;">.knownSuperclasses.values().iterator(); it.hasNext();) {
                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (configClass.equals(it.next())) {
                    it.remove();
                }
            }
        }
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Recursively process the configuration class and its superclass hierarchy.</span>
    SourceClass sourceClass =<span style="color: #000000;"> asSourceClass(configClass);
    </span><span style="color: #0000ff;">do</span><span style="color: #000000;"> {
       <span style="color: #ff0000;"> sourceClass </span></span><span style="color: #ff0000;">=</span><span style="color: #000000;"><span style="color: #ff0000;"> doProcessConfigurationClass(configClass, sourceClass);//处理定义的配置类</span>
    }
    </span><span style="color: #0000ff;">while</span> (sourceClass != <span style="color: #0000ff;">null</span><span style="color: #000000;">);

    </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.configurationClasses.put(configClass, configClass);
}<br><br></span></pre>
复制代码
复制代码
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Recursively process any member (nested) classes first</span>

processMemberClasses(configClass, sourceClass);

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Process any @PropertySource annotations</span>
    <span style="color: #0000ff;">for</span><span style="color: #000000;"> (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,
            org.springframework.context.annotation.PropertySource.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">)) {
        </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.environment <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            logger.warn(</span>"Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment"<span style="color: #000000;">);
        }
    }

    </span><span style="color: #ff0000;">// Process any @ComponentScan annotations<br></span>        Set&lt;AnnotationAttributes&gt; componentScans =<span style="color: #000000;"> AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.</span><span style="color: #0000ff;">class</span>, ComponentScan.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
    </span><span style="color: #0000ff;">if</span> (!componentScans.isEmpty() &amp;&amp;
            !<span style="color: #0000ff;">this</span><span style="color: #000000;">.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (AnnotationAttributes componentScan : componentScans) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> The config class is annotated with @ComponentScan -&gt; perform the scan immediately</span>
            Set&lt;BeanDefinitionHolder&gt; scannedBeanDefinitions =
                    <span style="color: #0000ff;">this</span><span style="color: #000000;">.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Check the set of scanned definitions for any further config classes and parse recursively if needed</span>
            <span style="color: #0000ff;">for</span><span style="color: #000000;"> (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (ConfigurationClassUtils.checkConfigurationClassCandidate(
                        holder.getBeanDefinition(), </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.metadataReaderFactory)) {
                    parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Process any @Import annotations</span>
    <span style="color: #ff0000;">processImports(configClass, sourceClass, getImports(sourceClass), true);//处理注解导入的类型

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

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Process individual @Bean methods</span>
    Set&lt;MethodMetadata&gt; beanMethods =<span style="color: #000000;"> retrieveBeanMethodMetadata(sourceClass);
    </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> BeanMethod(methodMetadata, configClass));
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Process default methods on interfaces</span>

processInterfaces(configClass, sourceClass);

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Process superclass, if any</span>
    <span style="color: #0000ff;">if</span><span style="color: #000000;"> (sourceClass.getMetadata().hasSuperClass()) {
        String superclass </span>=<span style="color: #000000;"> sourceClass.getMetadata().getSuperClassName();
        </span><span style="color: #0000ff;">if</span> (!superclass.startsWith("java") &amp;&amp; !<span style="color: #0000ff;">this</span><span style="color: #000000;">.knownSuperclasses.containsKey(superclass)) {
            </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.knownSuperclasses.put(superclass, configClass);
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> Superclass found, return its annotation metadata and recurse</span>
            <span style="color: #0000ff;">return</span><span style="color: #000000;"> sourceClass.getSuperClass();
        }
    }

    </span><span style="color: #008000;">//</span><span style="color: #008000;"> No superclass -&gt; processing is complete</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span><span style="color: #000000;">;
}</span></pre>
复制代码

 以上两个方法中标红的就是关键点。而且spring的大师们也把注释写的十分明显:”//Process any @Import annotations“,到这里已经彻底豁然开朗!

spring会先去处理scan,将你程序内部的所有要注册的Bean全部获得(自然包括那些configuration),这里统称为ConfigurationClass,scan全部整理完毕后才会去处理@Import注解时导入的类!

我们回到最初的问题 DataSourceRegister和A两个类为什么A成为了Bean但DataSourceRegister却未成为Bean呢?

答案就在processImports方法中,很明显candidate.isAssignable(ImportBeanDefinitionRegistrar.class)时操作为:configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());而普通的类通过processConfigurationClass(candidate.asConfigClass(configClass));方法,最终会被放在ConfigurationClassParser类的成员变量configurationClasses中,最终被初始化为Bean。。。

复制代码
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (importCandidates.isEmpty()) {
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
    }

    </span><span style="color: #0000ff;">if</span> (checkForCircularImports &amp;&amp;<span style="color: #000000;"> isChainedImportOnStack(configClass)) {
        </span><span style="color: #0000ff;">this</span>.problemReporter.error(<span style="color: #0000ff;">new</span> CircularImportProblem(configClass, <span style="color: #0000ff;">this</span><span style="color: #000000;">.importStack));
    }
    </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.importStack.push(configClass);
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            </span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (SourceClass candidate : importCandidates) {
                </span><span style="color: #0000ff;">if</span> (candidate.isAssignable(ImportSelector.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) {
                    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Candidate class is an ImportSelector -&gt; delegate to it to determine imports</span>
                    Class&lt;?&gt; candidateClass =<span style="color: #000000;"> candidate.loadClass();
                    ImportSelector selector </span>= BeanUtils.instantiateClass(candidateClass, ImportSelector.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
                    ParserStrategyUtils.invokeAwareMethods(
                            selector, </span><span style="color: #0000ff;">this</span>.environment, <span style="color: #0000ff;">this</span>.resourceLoader, <span style="color: #0000ff;">this</span><span style="color: #000000;">.registry);
                    </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.deferredImportSelectors != <span style="color: #0000ff;">null</span> &amp;&amp; selector <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> DeferredImportSelector) {
                        </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.deferredImportSelectors.add(
                                </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
                        String[] importClassNames </span>=<span style="color: #000000;"> selector.selectImports(currentSourceClass.getMetadata());
                        Collection</span>&lt;SourceClass&gt; importSourceClasses =<span style="color: #000000;"> asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, </span><span style="color: #0000ff;">false</span><span style="color: #000000;">);
                    }
                }
                </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (candidate.isAssignable(ImportBeanDefinitionRegistrar.<span style="color: #0000ff;">class</span><span style="color: #000000;">)) {
                    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Candidate class is an ImportBeanDefinitionRegistrar -&gt;
                    </span><span style="color: #008000;">//</span><span style="color: #008000;"> delegate to it to register additional bean definitions</span>
                    Class&lt;?&gt; candidateClass =<span style="color: #000000;"> candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar </span>=<span style="color: #000000;">
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, </span><span style="color: #0000ff;">this</span>.environment, <span style="color: #0000ff;">this</span>.resourceLoader, <span style="color: #0000ff;">this</span><span style="color: #000000;">.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
                    </span><span style="color: #008000;">//</span><span style="color: #008000;"> Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -&gt;
                    </span><span style="color: #008000;">//</span><span style="color: #008000;"> process it as an @Configuration class</span>
                    <span style="color: #0000ff;">this</span><span style="color: #000000;">.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (BeanDefinitionStoreException ex) {
            </span><span style="color: #0000ff;">throw</span><span style="color: #000000;"> ex;
        }
        </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Throwable ex) {
            </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> BeanDefinitionStoreException(
                    </span>"Failed to process import candidates for configuration class [" +<span style="color: #000000;">
                    configClass.getMetadata().getClassName() </span>+ "]"<span style="color: #000000;">, ex);
        }
        </span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
            </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.importStack.pop();
        }
    }
}</span></pre>
复制代码

简单了剖析了下spring-context代码,心情还是很不错滴!你明白这个问题了吗?

原文地址:https://www.cnblogs.com/zzq-include/p/8004506.html
原文地址:https://www.cnblogs.com/jpfss/p/11081367.html