springboot源码学习02-springboot的准备环境方法prepareEnvironment

基于springboot2.1.8。
废话少说我们上代码:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		//创建并配置环境 下面有代码跟踪
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境 下面有代码跟踪
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//
		ConfigurationPropertySources.attach(environment);
		//激活环境准备完毕事件 遍历所有实现了SpringApplicationRunListener接口的类。其中就有EventPublishingRunListener ,而EventPublishingRunListener其实会发出事件,调用监听了本事件的ApplicationListener接口的类。
		//这里会激活几个实现ApplicationListener接口的类 比如:ConfigFileApplicationListener SpringApplicationJsonEnvironmentPostProcessor 等
		//其中ConfigFileApplicationListener就是添加名叫random的RandomValuePropertySource到名叫systemEnvironment的PropertySource后;并初始化Profiles;初始化PropertiesPropertySourceLoader和YamlPropertySourceLoader这两个加载器从file:./config/,file:./,classpath:/config/,classpath:/路径下加载配置文件,PropertiesPropertySourceLoader加载配置文件application.xml和application.properties,YamlPropertySourceLoader加载配置文件application.yml和application.yaml。目前我们之后classpath:/路径下有个application.yml配置文件,将其属性配置封装进了一个名叫applicationConfig:[classpath:/application.yml]的OriginTrackedMapPropertySource中,并将此对象放到了propertySourceList的最后
		//我们画一下方法执行流程ConfigFileApplicationListener的 onApplicationEvent方法会被事件调用,onApplicationEvent方法中 主要有两个方法:onApplicationEnvironmentPreparedEvent和onApplicationPreparedEvent。 onApplicationEnvironmentPreparedEvent -> postProcessor.postProcessEnvironment ->addPropertySources -> new Loader(environment, resourceLoader).load()
		//onApplicationEvent方法->addPostProcessors ->context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(context)); 这块就是想环境中加入了BeanFactoryPostProcessor。后期上下文刷新refresh()方法里面会调用到。
		listeners.environmentPrepared(environment);
		//生成一个binder 把环境 转换器 等绑定到一起
		bindToSpringApplication(environment);
		// 验证环境 是web环境强制转成StandardEnvironment 否则使用转换器转
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		//
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

我们先看getOrCreateEnvironment方法,根据前面的web类型返回对应的环境对象,web类型通常为SERVLET所以返回new StandardServletEnvironment()。

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			//加载环境
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

我们可以跟踪代码
然后我们接着看configureEnvironment方法。上代码:

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		if (this.addConversionService) {
			//获得一个数据转换器conversionService 
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			//把转换器set到环境中
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}

继续分析下一个方法:

public static void attach(Environment environment) {
    // 判断environment是否是ConfigurableEnvironment的实例
    Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
    //获取PropertySources
    MutablePropertySources sources = ((ConfigurableEnvironment) environment)
            .getPropertySources();
    PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
    if (attached != null && attached.getSource() != sources) {
        sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
        attached = null;
    }
    if (attached == null) {
        // 将sources封装成ConfigurationPropertySourcesPropertySource对象,并把这个对象放到sources的第一位置
        sources.addFirst(new ConfigurationPropertySourcesPropertySource(
                ATTACHED_PROPERTY_SOURCE_NAME,
                new SpringConfigurationPropertySources(sources)));
    }
}
<think>好的,我现在需要帮助用户理解从`SpringApplication.run(Application.class, args);`开始的Spring Boot启动流程。用户之前已经询问过Bean的创建、初始化和赋值的源码,现在他们可能想进一步了解整个应用的启动过程,特别是Spring Boot是如何启动并初始化应用的。 首先,我需要回忆一下Spring Boot的启动流程。记得`SpringApplication.run()`是入口方法,它会触发自动配置、上下文初始化、Bean加载等步骤。用户可能对底层机制感兴趣,比如如何加载配置、如何启动内嵌服务器,或者整个生命周期中的关键步骤。 接下来,我需要确定用户的背景。他们可能是一个开发者,正在学习Spring Boot,或者在工作中需要调试启动问题。因此,我需要详细但清晰地解释每个阶段,包括关键类和方法,同时避免过于晦涩的术语。 我需要组织回答的结构。可能按照启动流程的顺序,从创建SpringApplication实例开始,到准备环境、创建上下文、刷新上下文,最后启动内嵌服务器。每个步骤要说明做了什么,涉及哪些核心类,以及它们的作用。 还要注意用户之前的提问中提到了源码解析,所以这次回答可能需要引用具体的源码位置,比如`SpringApplication`类的构造方法、`run()`方法的具体实现,以及`AbstractApplicationContext`的`refresh()`方法。这能帮助用户更深入地理解内部机制。 另外,用户可能对自动配置的原理感兴趣,比如`@SpringBootApplication`背后的机制,如何加载`spring.factories`,以及条件注解如何工作。这部分需要简要提及,但不要过于深入,以免偏离启动流程的主线。 还需要考虑性能优化和常见问题,比如启动时的监听器、事件机制,或者如何自定义启动过程。但根据用户当前的问题,可能暂时不需要展开这些,除非用户后续有更多问题。 最后,我应该使用图表或流程图来辅助说明,比如Mermaid的流程图,帮助用户可视化整个流程。同时,代码示例和源码位置的引用能增加回答的可信度和实用性。 总结一下,回答的结构应该是: 1. 入口方法`run()`的作用和两个阶段。 2. 详细分解每个阶段,包括实例创建、环境准备、上下文创建和刷新。 3. 关键扩展点,如监听器、自动配置、Bean加载。 4. 图示和源码引用,帮助用户理解。 5. 可能的后续步骤或相关知识点,引导用户进一步学习。 需要确保语言简洁,逻辑清晰,逐步引导用户理解整个启动过程。同时,注意使用中文术语,避免直译导致的歧义。</think>以下是Spring Boot应用启动过程的核心源码解析(基于Spring Boot 2.5.x版本): --- ### 一、启动流程全景图 ```mermaid graph TD A[run()入口] --> B[创建SpringApplication实例] B --> C[执行run方法] C --> D[准备环境] D --> E[创建应用上下文] E --> F[刷新上下文] F --> G[执行Runners] ``` --- ### 二、启动源码深度解析 #### 1. 入口方法`SpringApplication.run()` **源码位置**:`SpringApplication.java` ```java public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); // 分两个阶段 } ``` --- ### 三、SpringApplication实例化阶段 #### 1. 构造方法核心逻辑 ```java public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 1. 设置主配置类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 2. 推断WEB应用类型(Servlet/Reactive) this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 3. 加载初始化器(META-INF/spring.factories) setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 4. 加载监听器(关键事件监听) setListeners(getSpringFactoriesInstances(ApplicationListener.class)); // 5. 推断主启动类 this.mainApplicationClass = deduceMainApplicationClass(); } ``` --- ### 四、run()方法执行阶段 #### 1. 完整执行流程 **源码位置**:`SpringApplication#run` ```java public ConfigurableApplicationContext run(String... args) { // 阶段1:启动准备 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 获取运行监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); // 发送ApplicationStartingEvent try { // 阶段2:环境准备 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 阶段3:创建上下文 context = createApplicationContext(); // 阶段4:上下文预处理 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 阶段5:刷新上下文(核心!) refreshContext(context); // 阶段6:后置处理 afterRefresh(context, applicationArguments); stopWatch.stop(); // 发送ApplicationReadyEvent listeners.started(context); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } return context; } ``` --- ### 五、关键阶段详解 #### 1. 环境准备prepareEnvironment) ```java private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建环境(根据WEB类型创建对应环境) ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置PropertySources和Profiles configureEnvironment(environment, applicationArguments.getSourceArgs()); // 发送EnvironmentPreparedEvent listeners.environmentPrepared(environment); // 绑定Spring Boot配置(将环境变量绑定到SpringBootApplication对象) bindToSpringApplication(environment); return environment; } ``` #### 2. 上下文创建(createApplicationContext) ```java protected ConfigurableApplicationContext createApplicationContext() { // 根据webApplicationType选择上下文类型 return this.applicationContextFactory.create(this.webApplicationType); } // 默认实现(AnnotationConfigServletWebServerApplicationContext等) static { ApplicationContextFactory DEFAULT = (webApplicationType) -> { switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } }; } ``` #### 3. 上下文刷新(refreshContext) **核心源码位置**:`AbstractApplicationContext#refresh` ```java public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新(记录启动时间、初始化属性源等) prepareRefresh(); // 2. 获取新的BeanFactory(重要!) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. BeanFactory预处理(注册环境相关的Bean) prepareBeanFactory(beanFactory); // 4. BeanFactory后处理(加载配置类、组件扫描等) postProcessBeanFactory(beanFactory); // 5. 执行BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册BeanPostProcessor registerBeanPostProcessors(beanFactory); // 7. 初始化MessageSource(国际化) initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 子类扩展方法(如创建Web服务器) onRefresh(); // 10. 注册监听器 registerListeners(); // 11. 完成Bean初始化(重点!实例化所有非懒加载单例Bean) finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新(发布ContextRefreshedEvent) finishRefresh(); } } ``` --- ### 六、自动配置原理 #### 1. @SpringBootApplication注解 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Configuration @EnableAutoConfiguration // 核心注解 @ComponentScan public @interface SpringBootApplication { // ... } ``` #### 2. 自动配置触发流程 ```mermaid sequenceDiagram participant A as @EnableAutoConfiguration participant B as AutoConfigurationImportSelector participant C as spring.factories participant D as @Conditional条件判断 A->>B: 选择导入配置 B->>C: 加载META-INF/spring.factories C->>B: 返回自动配置类列表 B->>D: 执行条件注解过滤 D->>B: 返回有效配置类 B->>Spring: 注册有效配置类 ``` --- ### 七、内嵌Web服务器启动 #### 1. Web服务器创建流程 ```java // ServletWebServerApplicationContext protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 创建Web服务器 } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { // 从BeanFactory获取ServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 创建WebServer(Tomcat/Jetty/Undertow) this.webServer = factory.getWebServer(getSelfInitializer()); } ``` --- ### 八、启动后扩展点 #### 1. 常用扩展接口 | 接口类型 | 执行时机 | 典型用途 | |---------------------------|----------------------------|--------------------------| | ApplicationRunner | 上下文刷新完成后 | 启动后执行初始化任务 | | CommandLineRunner | 同ApplicationRunner | 命令行参数处理 | | ApplicationListener | 监听特定事件 | 监控启动过程 | | BeanPostProcessor | Bean初始化前后 | Bean增强处理 | ```java // 示例:ApplicationRunner实现 @Component public class MyRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { System.out.println("应用启动完成!"); } } ``` --- ### 九、性能优化建议 1. **减少组件扫描范围**: ```properties spring.component-scan.base-packages=com.example.core ``` 2. **延迟初始化**: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.setLazyInitialization(true); // 启用全局延迟初始化 app.run(args); } } ``` 3. **排除不必要的自动配置**: ```java @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, CacheAutoConfiguration.class }) ``` --- ### 十、调试技巧 在启动时添加JVM参数: ```bash -Dlogging.level.org.springframework=DEBUG -Ddebug # 打印自动配置报告 ``` 通过这个完整的启动流程分析,可以清晰看到Spring Boot如何通过约定大于配置的原则,将复杂的Spring生态整合为简单易用的启动过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值