Springboot run方法-run初始化启动(三)

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		//SpringApplication的启动由两部分组成:
		//1. 实例化SpringApplication对象
		//2. run(args):调用run方法
		return new SpringApplication(primarySources).run(args);
	}

项目初始化启动

上一篇文章分析完SpringApplication的实例化对象的创建过程,接着查看run(args)的项目初始化源码分析

public ConfigurableApplicationContext run(String... args) {
	    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 headless 属性
		configureHeadlessProperty();


		//   (1)获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 创建  ApplicationArguments 对象 初始化默认应用参数类
			// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			//(2)项目运行环境Environment的预配置
			// 创建并配置当前SpringBoot应用将要使用的Environment
			// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

			configureIgnoreBeanInfo(environment);
			// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
			Banner printedBanner = printBanner(environment);

			// (3)创建Spring容器
			context = createApplicationContext();
			// 获得异常报告器 SpringBootExceptionReporter 数组
			//这一步的逻辑和实例化初始化器和监听器的一样,
			// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);


			// (4)Spring容器前置处理
			//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

			// (5):刷新容器
			refreshContext(context);

			// (6):Spring容器后置处理
			//扩展接口,设计模式中的模板方法,默认为空实现。
			// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
			afterRefresh(context, applicationArguments);
			// 停止 StopWatch 统计时长
			stopWatch.stop();
			// 打印 Spring Boot 启动的时长日志。
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// (7)发出结束执行的事件通知
			listeners.started(context);

			// (8):执行Runners
			//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
			//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
			//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
		    // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

        //   (9)发布应用上下文就绪事件
		//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
		// 这样整个Spring Boot项目就正式启动完成了。
		try {
			listeners.running(context);
		} catch (Throwable ex) {
            // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
            handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		 //返回容器
		return context;
	}

根据源码分析,大致可以分为以下几个步骤:

第一步 获取并启动监听器

this.getRunListeners(args)޾listeners.starting()方法主要用于获取SpringApplication实例化过程中初始化的SpringApplicationRunListener监听器并运行


	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 这里仍然利用了getSpringFactoriesInstances方法来获取实例,
		// 从META-INF/spring.factories中读取Key为org.springframework.boot.SpringApplicationRunListener的Values
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

第二步 根据SpringApplicationRunListener以及参数来准备环境

this.prepareEnvironment(listeners, applicationArguments)主要用于对项目运行环境进行预设置,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的环境

/*
	加载外部化配置资源到environment,包括命令行参数、servletConfigInitParams、
	servletContextInitParams、systemProperties、sytemEnvironment、random、
	application.yml(.yaml/.xml/.properties)等;初始化日志系统。
	 */
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {

		//获取或创建环境(存在就直接返回,不存在创建一个再返回)
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境:配置PropertySources和active Profiles
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。
		listeners.environmentPrepared(environment);
		//将环境绑定到SpringApplication
		bindToSpringApplication(environment);
		//如果非web环境,将环境转换成StandardEnvironment
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// 配置PropertySources对它自己的递归依赖
		// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

第三步 创建Spring容器

根据webApplicationType进行判断,确定容器类型,如果该类型为SERVLET,会通过反射装载对应的字节码,也就是,AnnotationConfigServletWebServerApplicationContext,接着使用之前初始化设置的context(应用上下文)environment(项目运行环境)listeners(监听器)applicationArguments(项目参数)printedBanner(项目图标信息)进行应用上线文的组装配置,并刷新

protected ConfigurableApplicationContext createApplicationContext() {
	    // 根据 webApplicationType 类型,获得 ApplicationContext 类型
		// 这里创建容器的类型 还是根据webApplicationType进行判断的,
		// 该类型为SERVLET类型,所以会通过反射装载对应的字节码,
		// 也就是AnnotationConfigServletWebServerApplicationContext

		// 先判断有没有指定的实现类
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {

				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			} catch (ClassNotFoundException ex) {
				throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
			}
		}
		// 创建 ApplicationContext 对象
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

第四步 Spring容器前置处理

这一步主要实在容器刷新之前的准备工作,设置容器环境,包括各种变量参数等,其中包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置奠定基础


	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//设置容器环境,包括各种变量
	    context.setEnvironment(environment);

		//设置上下文的 bean 生成器和资源加载器
		postProcessApplicationContext(context);

		//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
		applyInitializers(context);

		//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
		listeners.contextPrepared(context);

		//记录启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		// 加载所有资源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
		load(context, sources.toArray(new Object[0]));

		//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
        listeners.contextLoaded(context);

		//这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等
	}

第五步 刷新容器

开启刷新Spring容器,通过refresh方法对整个IOC容器的初始化

private void refreshContext(ConfigurableApplicationContext context) {
	    // 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
		refresh(context);
		// 注册 ShutdownHook 钩子
		if (this.registerShutdownHook) {
			try {
				//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
				context.registerShutdownHook();
			} catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

第六步 Spring容器的后置处理

扩展接口,设计模式中的模板方法,默认为空实现。
如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理

第七步 发出结束执行的事件

获取EventPublishingRunListener监听器,并执行器started方法,并且将其创建的spring容器传进去,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext的publishEvent方法也就是说在这里实在spring容器发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的staring是直接向SpringApplication中的监听器发布启动事件


	public void started(ConfigurableApplicationContext context) {
		//执行所有SpringApplicationRunListener实现的started方法。
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

第八步 执行Runners

用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口


	private void callRunners(ApplicationContext context, ApplicationArguments args) {
	    // 获得所有 Runner 们
		List<Object> runners = new ArrayList<>();
		// 获得所有 ApplicationRunner 实现类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// 获得所有 CommandLineRunner 实现类
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		// 排序 runners
		AnnotationAwareOrderComparator.sort(runners);
		// 遍历 Runner 数组,执行逻辑
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

第九步 发布应用上下文就绪事件

表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
这样整个Spring Boot项目就正式启动完成了。

	public void running(ConfigurableApplicationContext context) {
		//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时小浅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值