首页 > 其他分享 >SpringBoot加载自动配置类的流程

SpringBoot加载自动配置类的流程

时间:2022-10-07 23:24:06浏览次数:77  
标签:return SpringBoot 流程 ex context new configurations class 加载

自定义过starter的同学应该都知道,自动配置类需要用 EnableAutoConfiguration 注解修饰,并且需要将自动配置类配置在spring.factories中。但自动配置类是如何被SpringBoot加载的呢?

下面结合SpringBoot的启动流程,记述一下自动配置类的加载过程。

首先,从SpringBoot项目的启动类的SpringBootApplication.run(#,#)方法查看源码,可以看到最终执行的是以下代码:

/**
      * Run the Spring application, creating and refreshing a new
      * {@link ApplicationContext}.
      * @param args the application arguments (usually passed from a Java main method)
      * @return a running {@link ApplicationContext}
      */
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                 applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments,
                       printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

可以看到SpringBoot启动时的主要内容是创建Environment、ApplicationContext以及对应事件的发布。代码详情,这里不涉及。我们这关注SpringBoot是在哪一步加载自动配置类的。而这一步就是第28行的 refreshContext(context); 追踪 refreshContext(context); 可以发现,实际执行的代码是 AbstractApplicationContext 的 refresh 方法, 代码如下:

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

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

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

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

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

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

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

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

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

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

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

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

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

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

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

            // Propagate exception to caller.
            throw ex;
        }

关注第18行的 invokeBeanFactoryPostProcessors(beanFactory); ,这一步会执行实现了 BeanFactoryPostProcessor 接口的类;这个接口是Spring初始化bean时对外暴露的入口,它可以修改bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性。追踪代码,可以看到实际执行的是 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors) 方法。这个方法比较复杂,分为了好几部分去处理,截取其中我们关心的部分即可(其实还包含了优先级、属性等类似处理过程)。

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

第18行的代码是我们要关注的,在这一步时,currentRegistryProcessors只有一个元素: ConfigurationClassPostProcessor ; 下一步执行的具体代码就是 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法;然后执行到 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 方法,这个方法内容比较多,也比较复杂,这里也只贴出我们需要关注的几行代码:

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
    this.metadataReaderFactory, this.problemReporter, this.environment,
    this.resourceLoader, this.componentScanBeanNameGenerator, registry);

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

这里new了一个 ConfigurationClassParser 实例,并且执行了它的 parse(Set configCandidates) 方法, 注意这里传入的参数虽然是一个Set,但实际只有我们的启动类一个元素。这个方法的代码如下:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        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);
        }
    }

    this.deferredImportSelectorHandler.process();
}

在这个方法里,实际执行了第6行的代码。这里不是我们的目标,先不关注;注意第24行的代码,好像和我们看到了文章开头提到的 AutoConfigurationImportSelector 有点关联了,没错,这里就是实际加载自动配置类的地方。在这个process方法里,可以看到这里的importSelector元素就是 AutoConfigurationImportSelector ,到了这里我们有理由推测,SpringBoot是查找所有由 AutoConfigurationImportSelector 修饰的类来加载自动配置类的。继续往下追踪代码,关注 getImports 方法:

/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                           deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

关注第7行内的process方法,点进去会发现这是 DeferredImportSelector 类的内部接口 Group 的接口方法,它的实现类有两个: AutoConfigurationImportSelector的内部类AutoConfigurationGroup 和 ConfigurationClassParser的内部类DefaultDeferredImportSelectorGroup ;这里执行到的代码是 AutoConfigurationGroup ,代码如下:

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                 () -> String.format("Only %s implementations are supported, got %s",
                                     AutoConfigurationImportSelector.class.getSimpleName(),
                                     deferredImportSelector.getClass().getName()));
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

可以看到,这里传入的参数必须是 AutoConfigurationImportSelector 实例,否则会抛出异常,所以EnableAutoConfiguration 注解类必须由 @Import(AutoConfigurationImportSelector.class) 修饰;进入第8行的 getAutoConfigurationEntry 方法:

/**
      * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
      * of the importing {@link Configuration @Configuration} class.
      * @param autoConfigurationMetadata the auto-configuration metadata
      * @param annotationMetadata the annotation metadata of the configuration class
      * @return the auto-configurations that should be imported
      */
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

从方法名可以看到这里有加载配置类候选的逻辑,有过滤掉不需要加载的逻辑。我们这关注加载配置候选的逻辑,关于过滤(即条件加载机制)可以参考这篇文章 https://blog.csdn.net/weixin_33915554/article/details/89702088 ; 进入第14行的 getCandidateConfigurations 方法,如果看过SpringBoot源码的同学,就会有一种熟悉的感觉

/**
      * Return the auto-configuration class names that should be considered. By default
      * this method will load candidates using {@link SpringFactoriesLoader} with
      * {@link #getSpringFactoriesLoaderFactoryClass()}.
      * @param metadata the source metadata
      * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
      * attributes}
      * @return a list of candidate configurations
      */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),                                                                                       getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

这里的 SpringFactoriesLoader.loadFactoryNames 方法,就是根据类名从spring.factories中查找对应的实现类名。我们还可以看一下 getSpringFactoriesLoaderFactoryClass 的代码:

/**
      * Return the class used by {@link SpringFactoriesLoader} to load configuration
      * candidates.
      * @return the factory class
      */
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

没错,返回的就是 EnableAutoConfiguration.class 。好了,现在我们真实的从源码看到了一些文章说的:EnableAutoConfiguration 注解类由 @Import(AutoConfigurationImportSelector.class) 修饰, SpringBoot从spring.facories中加载自动配置类的说法。

到这里为止,我们已经知道了SpringBoot获取自动配置类全限定名的过程。回到 ConfigurationClassParser.DeferredImportSelectorGroupingHandler的processGroupImports 方法,继续看拿到所有的自动配置类名后的操作,也就是forEach里面的内容,它解决一些循环依赖的问题,最终将所有的自动配置类信息都放到了 configurationClasses 中。 ConfigurationClassParser 的parse方法执行完毕后,回到 ConfigurationClassPostProcessor的processConfigBeanDefinitions 方法,继续看下面一部分代码。

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
    this.metadataReaderFactory, this.problemReporter, this.environment,
    this.resourceLoader, this.componentScanBeanNameGenerator, registry);

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

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

第21行的 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitions 方法,将自动配置类解析成beanDefination,注册到 registry 中;看一下代码:

/**
      * Read a particular {@link ConfigurationClass}, registering bean definitions
      * for the class itself and all of its {@link Bean} methods.
      */
private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, 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());
        return;
    }

    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

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

自动配置类走的是第18行的逻辑,后续的功能主要是一些装配BeanDefination的工作,这里不再详述。

文章转载:http://t.zoukankan.com/zsxneil-p-14043443.html

标签:return,SpringBoot,流程,ex,context,new,configurations,class,加载
From: https://www.cnblogs.com/likeguang/p/16767488.html

相关文章