SpringApplication 的运行过程分析: run()

本文详细解析了Spring Boot应用程序启动的全过程,从SpringApplicationRunListener的实例化开始,到ApplicationContext的创建、准备、刷新,再到Runners的执行,直至最终发布ApplicationReadyEvent事件。

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

源代码版本 Spring 1.5.8.RELEASE

1.获取 SpringApplicationRunListener 实例
比如 EventPublishingRunListener
// 内部使用SpringFactoriesLoader 加载所有定义的 SpringApplicationRunListener并构造实例
// 可以将 SpringApplicationRunListeners 理解为一组这样的实例的集合
SpringApplicationRunListeners listeners = getRunListeners(args);
2.向所有 SpringApplicationRunListener 实例通知事件 ApplicationStartedEvent
listeners.starting();
3. 准备环境 Environment
	// 准备 Environment : 创建并配置 Enviroment
	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 向所有 SpringApplicationRunListener 实例通知事件 ApplicationEnvironmentPreparedEvent
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}
	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webEnvironment) {
			// 如果是 Web 环境,创建 StandardServletEnvironment 对象
			return new StandardServletEnvironment();
		}
		// 如果是非 Web 环境,创建 StandardEnvironment 对象
		return new StandardEnvironment();
	}	
	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}	
4. 向所有 SpringApplicationRunListener 实例通知事件 ApplicationEnvironmentPreparedEvent
5. 创建 ApplicationContext
	/**
	 * 非web环境应用上下文的实现类
	 * The class name of application context that will be used by default for non-web
	 * environments.
	 */
	public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

	/**
	 * web环境应用上下文的实现类
	 * The class name of application context that will be used by default for web
	 * environments.
	 */
	public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
	
	// 初始化过程中已经推断过当前应用是否Web应用环境,
	//下面根据当前应用是否Web应用环境找到相应的ApplicationContext实现类,
	//AnnotationConfigEmbeddedWebApplicationContext对应非Web环境 
	//或者 ConfigurableWebApplicationContext对应Web环境
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		// 创建 ApplicationContext 实例
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

ApplicationContext对象实例化的过程只是做了一个Java对象创建的动作吗 ? 并不是那么简单,以缺省Web应用的情况来看 :

	public AnnotationConfigEmbeddedWebApplicationContext() {
		//创建一个AnnotatedBeanDefinitionReader,注解Bean类程序方式注册的便捷工具,和
		//classpath Bean定义扫描器ClassPathBeanDefinitionScanner互为补充,采用同样的
		//注解解决机制,但是只针对显式注册的类
		//AnnotatedBeanDefinitionReader()构造方法内部会进一步调用工具类
		//AnnotationConfigUtils. registerAnnotationConfigProcessors(registry) 
		//注册 6个BeanPostProcessor Bean定义,这也是容器中最早出现的一组Bean定义: 
		//1. annotation.internalConfigurationAnnotationProcessor
		//2. annotation.internalAutowiredAnnotationProcessor
		//3. annotation.internalRequiredAnnotationProcessor
		//4. annotation.internalCommonAnnotationProcessor
		//5. event.internalEventListenerProcessor
		//6. event.internalEventListenerFactory
		this.reader = new AnnotatedBeanDefinitionReader(this);
		// Bean definition 扫描器 : 
		// 扫描classpath上的Bean定义并注册到 ApplicationContext 或者 BeanFactory
		// Q : 什么样的会被认为是候选Bean定义 ?
		// A : @Component, @Repository,@Service,@Controller等Spring注解定义的类,或者 
		//     Java EE 6's javax.annotation.ManagedBean,JSR-330 javax.inject.Named 
		//     注解定义的类
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
6. 准备 ApplicationContext
	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 关联 ApplicationContext 和 Environment			
		context.setEnvironment(environment);
		// 1.如果外部指定了 Bean 名称生成器,将其作为单例Bean注册到容器,
		// 2.如果外部指定了资源加载器,设置 ApplicationContext 的资源加载器和相应的类加载器
		postProcessApplicationContext(context);
		// 执行每个 ApplicationContextInitializer 实例的初始化 initialize(context)
		applyInitializers(context);
		// 向所有 SpringApplicationRunListener 实例通知事件 ApplicationEnvironmentPreparedEvent
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 加载 Bean 定义, 只根据源类型将源设置到具体的 Bean定义加载器,并没有全面执行Bean定义加载.
		// 这里 sources 指定的类,如果有注解@Component,则会被作为Bean定义添加到容器中,比如当前
		// SpringApplication的入口类,因为有注解@SpringBootConfiguration,内含了注解@Component,
		// 所以它会作为作为一个Bean被注册进容器;
		load(context, sources.toArray(new Object[sources.size()]));
		// 1.向所有 SpringApplicationRunListener 实例通知事件 ApplicationPreparedEvent
		// 2.关联 ApplicationContext 和所有 ApplicationContextAware 的 ApplicationListener
		listeners.contextLoaded(context);
	}
7.刷新 ApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
8.执行 ApplicationContext 刷新后任务 : 执行 Runners

SpringApplication.run()中的代码片段。

afterRefresh(context, applicationArguments);
/**
	 * Called after the context has been refreshed.
	 * @param context the application context
	 * @param args the application arguments
	 */
	protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
		callRunners(context, args);
	}

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<Object>();
		// 找出所有的 ApplicationRunner Bean
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// 找出所有的 CommandLineRunner Bean
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		// 排序
		AnnotationAwareOrderComparator.sort(runners);
		// 调用所有的 Runner
		for (Object runner : new LinkedHashSet<Object>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}
9.向所有 SpringApplicationRunListener 实例通知事件 ApplicationReadyEvent
	// SpringApplicationRunListener 会进一步将此事件通知到 ApplicationContext 中去
	// Listeners have been registered to the application context so we should
	// use it at this point if we can
	context.publishEvent(event);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值