SpringBoot自动配置原理

本文深入探讨了SpringBoot应用的启动流程,包括创建SpringApplication对象和运行run方法。重点解析了SpringBoot自动配置的原理,通过EnableAutoConfiguration注解引入AutoConfigurationImportSelector组件,该组件在启动过程中调用process方法,处理自动配置类。同时,文章总结了SpringBoot应用启动的两个主要步骤和自动配置的核心步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringBoot自动配置原理


前言:这篇文章是在看完SpringBoot自动配置原理后记录的,如果有不对的地方烦请多多指正。

SpringBoot应用启动流程

通过调用追踪,我们发现SpringBoot应用启动时,它做了两件事:创建SpringApplication对象、运行run方法,源代码如下:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

我们接着来看,这两步,主要干了些啥。

  • 创建SpringApplication对象
public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判断当前应用是不是一个web应用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 从classpath下的meta-info/spring.factories文件中,找到所有配置的ApplicationContextInitializer,然后保存起来
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 从classpath下的的meta-info/spring.factories文件中,找到所有配置的ApplicationListener,然后保存起来
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 找到有main方法的主配置类,只有一个
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
  • 运行run方法
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        
        // 从classpath下的的meta-info/spring.factories文件中,找到所有配置的ApplicationRunListener,然后回调它们的starting方法
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // 准备环境,主要做了这几件事:创建环境、使用命令行参数配置环境和回调SpringApplicationRunListener#environmentPrepared方法
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);

			// 创建ioc容器
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            
            // 准备应用上下文,做了如下几件事:
            // ① 将环境应用到ioc容器
            // ②调用applyInitializers(ConfigurableApplicationContext context)方法,调用之前保存的ApplicationContextInitializer的initialize()方法
            // ③调用SpringApplicationRunListener#contextPrepared()方法
            // ④调用SpringApplicationRunListener#contextLoaded()方法
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			
			// 刷新容器,初始化ioc容器,在这里边会进行自动配置
            this.refreshContext(context);
            // 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner,执行它们的run方法
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

			// 执行所有的ApplicationContextListener的started()方法
            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

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

SpringBoot自动配置原理

我们都知道SpringBoot的自动配置是通过使用EnableAutoConfiguration注解开启,在这个注解里边它会为我们的Spring应用引入AutoConfigurationImportSelector组件,这个组件实现了DeferredImportSelector接口;在SpringBoot应用启动过程中,会调用ConfigurationClassParser类的parse(Set configCandidates)方法,这个方法里边最终会调用AutoConfigurationImportSelector的process方法来获取所有能够进行自动配置的配置类;我们来看看ConfigurationClassParser#parse(Set configCandidates)方法的源代码:(注意:ConfigurationClassParser类的外部调用入口在parse(Set configCandidates))

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);
			}
		}

		// 执行找到的DeferredImportSelector     -----             重点在这
		this.deferredImportSelectorHandler.process();
	}

this.deferredImportSelectorHandler.process()方法里边会调用所有找到的DeferredImportSelector的 process(AnnotationMetadata metadata, DeferredImportSelector selector)方法;执行完之后,再处理找到的自动配置类

整个调用链路如下图:
SpringBoot自动配置调用链路图

总结

  • SpringBoot应用启动流程,分两步:
  1. 创建SpringApplication对象
  2. 执行SpringApplication对象的run方法
  • SpringBoot自动配置原理

    SpringBoot应用自动配置原理是通过EnableAutoConfiguration注解开启的,这个注解里边为我们导入了一个组件:AutoConfigurationImportSelector,它实现了DefferedImportSelector接口;在SpringBoot应用启动过程中,刷新ioc容器时,会调用ConfigurationClassParser类的parse()方法,这个方法里边的最后一步会执行所有发现的DefferedImportSelector的process()方法,其中就包括了AutoConfigurationImportSelector组件的process方法;在AutoConfigurationImportSector方法的process()方法中,他会调用一个方法:getAutoConfigurationEntry()方法来获取满足条件的要进行自动配置的配置类;在这个getAutoConfigurationEntry()方法里边做了三件事:读取、去重和过滤;其中读取这一步调用了一个getCandidateConfiguration方法,在这个方法里边使用SpringBoot的SpringFactoriesLoader机制来读取classpath路径下的meta-info/spring.factories文件中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的value - 需要进行自动配置的配置类。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值