[spring6: SpringApplication.run]-应用启动

SpringApplication

java 复制代码
@SpringBootApplication
public class Application {
   public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

run

java 复制代码
public ConfigurableApplicationContext run(String... args) {
	Startup startup = Startup.create();
	
	// 默认启用
	if (this.properties.isRegisterShutdownHook()) {
		SpringApplication.shutdownHook.enableShutdownHookAddition();
	}
	
	// 创建 DefaultBootstrapContext,通过 bootstrapRegistryInitializers 列表中的初始化器对其进行配置,确保在应用启动前完成必要的设置
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	
	ConfigurableApplicationContext context = null;
	
	// 配置系统属性 java.awt.headless,决定是否启用无头模式
	// 该属性一般用于图形界面应用,默认是启用无头模式(headless),表示不需要图形界面支持
	configureHeadlessProperty();
	
	// 获取所有的 SpringApplicationRunListener 实例,并将它们封装成一个 SpringApplicationRunListeners 对象
	// 启动监听器用于监听应用启动的各个阶段
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 启动监听器并通知应用启动开始
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	
	try {
		// 封装传入的命令行参数 args 为 ApplicationArguments 对象
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 1. 创建 ApplicationServletEnvironment
		// 2. 设置 ApplicationConversionService
		// 3. 根据 args 配置 PropertySources,Profiles
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		
		// 根据 Banner.Mode 配置决定是否打印 banner,如果是 LOG 模式,则通过 logger 输出,否则输出到控制台(System.out)
		Banner printedBanner = printBanner(environment);
		
		// 创建 AnnotationConfigServletWebServerApplicationContext
		// GenericApplicationContext -> 创建 DefaultListableBeanFactory
		// AbstractApplicationContext -> 创建 PathMatchingResourcePatternResolver
		context = createApplicationContext();
		context.setApplicationStartup(this.applicationStartup);
		
		// 1. context 配置 environment
		// 2. context.beanFactory 配置 context.ConversionService
		// 3. ApplicationContextInitializer.initialize
		// 4. 添加特殊的单例bean: springApplicationArguments, springBootBanner,并设置beanFacotory 禁止循环依赖,禁止 Bean 定义覆盖
		// 5. 添加工厂处理器PropertySourceOrderingBeanFactoryPostProcessor,加载Sources
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

		refreshContext(context);

		// // 在刷新完成后,执行一些后处理逻辑,默认空实现
		afterRefresh(context, applicationArguments);
		
		startup.started();
		if (this.properties.isLogStartupInfo()) {
			new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
		}
		listeners.started(context, startup.timeTakenToStarted());
	
		// 按顺序执行所有 Runner 类型 Bean,并处理异常
		// Runner: CommandLineRunner, ApplicationRunner, JobLauncherApplicationRunner
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		throw handleRunFailure(context, ex, listeners);
	}
	try {
		if (context.isRunning()) {
			listeners.ready(context, startup.ready());
		}
	}
	catch (Throwable ex) {
		throw handleRunFailure(context, ex, null);
	}
	return context;
}

prepareEnvironment

推荐阅读:

  1. [spring6: PropertySource & PropertyResolver & Environment]-源码分析
  2. [spring6: ResolvableType & TypeDescriptor & ConversionService]-类型系统
java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// 创建 ApplicationServletEnvironment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 设置 ApplicationConversionService,根据 args 配置 PropertySources,Profiles
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	ConfigurationPropertySources.attach(environment);
	
	listeners.environmentPrepared(bootstrapContext, environment);
	
	// 将特定的 PropertySource 移到 PropertySources 列表的末尾
	ApplicationInfoPropertySource.moveToEnd(environment);
	DefaultPropertiesPropertySource.moveToEnd(environment);
	
	Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
	
	// 使用 Binder 来绑定 'spring.main' 配置到 'this.properties'
	bindToSpringApplication(environment);

	// 判断是否需要将一个 ConfigurableEnvironment 实例转换为目标类型的 ConfigurableEnvironment
	// 此处已是目标类型,直接返回
	if (!this.isCustomEnvironment) {
		EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
		// deduceEnvironmentClass(): ApplicationServletEnvironment.class
		environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}
java 复制代码
private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	// WebApplicationType.SERVLET
	WebApplicationType webApplicationType = this.properties.getWebApplicationType();
	ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(webApplicationType);
	if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
		environment = ApplicationContextFactory.DEFAULT.createEnvironment(webApplicationType);
	}
	return (environment != null) ? environment : new ApplicationEnvironment();
}
java 复制代码
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
	if (this.addConversionService) {
		environment.setConversionService(new ApplicationConversionService());
	}
	configurePropertySources(environment, args);
	configureProfiles(environment, args);
}

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
	MutablePropertySources sources = environment.getPropertySources();
	if (!CollectionUtils.isEmpty(this.defaultProperties)) {
		DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
	}
	if (this.addCommandLineProperties && args.length > 0) {
		String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
		if (sources.contains(name)) {
			PropertySource<?> source = sources.get(name);
			CompositePropertySource composite = new CompositePropertySource(name);
			composite
				.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
			composite.addPropertySource(source);
			sources.replace(name, composite);
		}
		else {
			sources.addFirst(new SimpleCommandLinePropertySource(args));
		}
	}
	environment.getPropertySources().addLast(new ApplicationInfoPropertySource(this.mainApplicationClass));
}

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
}

prepareContext

java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
	// context.beanFactory 配置 context.ConversionService
	postProcessApplicationContext(context);
	// 略,未执行
	addAotGeneratedInitializerIfNecessary(this.initializers);
	// 在 ApplicationContext 刷新前调用所有已配置的 ApplicationContextInitializer 实例
	// 使得开发者可以在应用启动时,对 ApplicationContext 进行个性化的初始化配置
	applyInitializers(context);
	
	listeners.contextPrepared(context);
	bootstrapContext.close(context);
	if (this.properties.isLogStartupInfo()) {
		logStartupInfo(context.getParent() == null);
		logStartupInfo(context);
		logStartupProfileInfo(context);
	}
	
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
		//是否允许 Bean 之间存在循环依赖, 默认为false
		// 如果设置为 true,Spring 容器会尝试自动解决循环依赖问题;
		// 如果设置为 false,则遇到循环依赖时会抛出异常
		autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
		if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
			// 是否允许重写现有的 Bean 定义,默认为false
			// 如果设置为 true,则可以注册具有相同名称的 Bean 定义,这样会覆盖先前的定义;
			// 如果设置为 false,则不允许覆盖,注册新的 Bean 定义时会抛出异常。
			listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
		}
	}
	// false
	if (this.properties.isLazyInitialization()) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// false
	if (this.properties.isKeepAlive()) {
		context.addApplicationListener(new KeepAlive());
	}
	context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
	
	// 当前没有使用 AOT 生成的优化代码,此时进入加载源的流程
	if (!AotDetector.useGeneratedArtifacts()) {
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined");
		load(context, sources.toArray(new Object[0]));
	}
	
	listeners.contextLoaded(context);
}

refreshContext

java 复制代码
private void refreshContext(ConfigurableApplicationContext context) {
	if (this.properties.isRegisterShutdownHook()) {
		// 注册 shutdownHook 为 JVM ShutdownHook
		// 当 JVM 进程开始终止时,所有注册的 Shutdown Hook 将会按先后顺序执行,实现应用程序优雅关闭
		shutdownHook.registerApplicationContext(context);
	}
	refresh(context);
}
java 复制代码
protected void refresh(ConfigurableApplicationContext applicationContext) {
	// ServletWebServerApplicationContext.refresh -> AbstractApplicationContext.refresh 
	applicationContext.refresh();
}

AbstractApplicationContext.refresh

推荐阅读:[spring6: AbstractApplicationContext.refresh()]-源码分析

java 复制代码
@Override
public void refresh() throws BeansException, IllegalStateException {
	this.startupShutdownLock.lock();
	try {
		this.startupShutdownThread = Thread.currentThread();

		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

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

			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			beanPostProcess.end();

			// 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 (RuntimeException | Error 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;
		}

		finally {
			contextRefresh.end();
		}
	}
	finally {
		this.startupShutdownThread = null;
		this.startupShutdownLock.unlock();
	}
}

afterRefresh

java 复制代码
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {}
相关推荐
老神在在0012 小时前
SpringMVC1
java·前端·学习·spring
程序猿小D2 小时前
[附源码+数据库+毕业论文+开题报告]基于Spring+MyBatis+MySQL+Maven+jsp实现的车辆运输管理系统,推荐!
java·数据库·mysql·spring·毕业设计·开题报告·车辆运输管理系统
Bug退退退1234 小时前
RabbitMQ 高级特性之消息分发
java·分布式·spring·rabbitmq
非ban必选9 小时前
spring-ai-alibaba之Rag 增强问答质量
java·人工智能·spring
Z_W_H_10 小时前
【SpringBoot】实战-开发接口-用户-注册
java·spring boot·spring
非ban必选10 小时前
spring-ai-alibaba官方 Playground 示例之联网搜索代码解析2
人工智能·python·spring
white camel11 小时前
重学SpringMVC一SpringMVC概述、快速开发程序、请求与响应、Restful请求风格介绍
java·后端·spring·restful
Jinkxs13 小时前
Spring MVC 执行流程详解:一次请求经历了什么?
java·spring·mvc
duration~14 小时前
Spring AI快速入门
java·人工智能·后端·spring·flask