Alian解读SpringBoot 2.6.0 源码(九):启动流程分析之应用上下文刷新后处理(启动完成事件,Runner运行器,就绪事件)

一、背景

  我们用他三篇文章我们解读应用上下文的刷新,对整个刷新的流程有个大致的轮廓了,本篇主要解读应用上下文刷新后的一些操作,老样子还是回顾下启动的整体流程,这样就能不迷路。

1.1、run方法整体流程

  接下来的几个方法所在类的具体路径:org.springframework.boot.SpringApplication

	public ConfigurableApplicationContext run(String... args) {
		// 1、记录启动的开始时间(单位纳秒)
		long startTime = System.nanoTime();
		
		// 2、初始化启动上下文、初始化应用上下文
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;

		// 3、设置无头属性:“java.awt.headless”,默认值为:true(没有图形化界面)
		configureHeadlessProperty();

		// 4、获取所有 Spring 运行监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 发布应用启动事件
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 5、初始化默认应用参数类(命令行参数)
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			// 6、根据运行监听器和应用参数 来准备 Spring 环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			// 配置忽略bean信息
			configureIgnoreBeanInfo(environment);

			// 7、创建 Banner 并打印
			Banner printedBanner = printBanner(environment);

			// 8、创建应用上下文
			context = createApplicationContext();
			// 设置applicationStartup
			context.setApplicationStartup(this.applicationStartup);

			// 9、准备应用上下文
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

			// 10、刷新应用上下文(核心)
			refreshContext(context);

			// 11、应用上下文刷新后置处理
			afterRefresh(context, applicationArguments);

			// 13、时间信息、输出日志记录执行主类名
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}

			// 14、发布应用上下文启动完成事件
			listeners.started(context, timeTakenToStartup);

			// 15、执行所有 Runner 运行器
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
			// 运行错误处理
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			// 16、发布应用上下文就绪事件(可以使用了)
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		} catch (Throwable ex) {
			// 运行错误处理
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		// 17、返回应用上下文
		return context;
	}

1.2、本文解读范围

  本文解读的范围如下:

	// 11、应用上下文刷新后置处理
	afterRefresh(context, applicationArguments);

	// 13、时间信息、输出日志记录执行主类名
	Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
	}

	// 14、发布应用上下文启动完成事件
	listeners.started(context, timeTakenToStartup);

	// 15、执行所有 Runner 运行器
	callRunners(context, applicationArguments);

	try {
		// 16、发布应用上下文就绪事件(可以使用了)
		Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
		listeners.ready(context, timeTakenToReady);
	} catch (Throwable ex) {
		// 运行错误处理
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	// 17、返回应用上下文
	return context;

二、应用上下文刷新后置处理

  此方法所在类的具体路径:org.springframework.boot.SpringApplication

	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}

  这个里后置刷新是一个空实现。

三、时间信息、输出日志记录执行主类名

	// 计算启动到刷新完的时间
	Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
	}

  我们先看下logStarted方法,此方法所在类的具体路径:org.springframework.boot.StartupInfoLogger

class StartupInfoLogger {
	private final Class<?> sourceClass;

	StartupInfoLogger(Class<?> sourceClass) {
		this.sourceClass = sourceClass;
	}

	void logStarted(Log applicationLog, Duration timeTakenToStartup) {
		if (applicationLog.isInfoEnabled()) {
			// 获取启动信息,并输出日志
			applicationLog.info(getStartedMessage(timeTakenToStartup));
		}
	}
	
	private CharSequence getStartedMessage(Duration timeTakenToStartup) {
		StringBuilder message = new StringBuilder();
		message.append("Started ");
		// 拼接子类信息
		appendApplicationName(message);
		message.append(" in ");
		// 启动主类的时间
		message.append(timeTakenToStartup.toMillis() / 1000.0);
		message.append(" seconds");
		try {
			// JVM 运行时间
			double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
			message.append(" (JVM running for ").append(uptime).append(")");
		} catch (Throwable ex) {
			// No JVM time available
		}
		return message;
	}

	private void appendApplicationName(StringBuilder message) {
		// 获取主类名
		String name = (this.sourceClass != null) ? ClassUtils.getShortName(this.sourceClass) : "application";
		message.append(name);
	}
}

  实际结果就相当于

Started SpringbootApplication in 1.3 seconds (JVM running for 4.738)

四、发布应用上下文启动完成事件

  此方法所在类的具体路径:org.springframework.boot.SpringApplicationRunListeners

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;
	
	void started(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken));
	}
	
	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, null);
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}
}

  此方法所在类的具体路径:org.springframework.boot.context.event.EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	@Override
	public void started(ConfigurableApplicationContext context, Duration timeTaken) {
		// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext
		// 初始化ApplicationStartedEvent
		// 通过应用上下文发布事件ApplicationStartedEvent
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
		// 通过AvailabilityChangeEvent发布事件
		AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
	}
}

4.1、ApplicationStartedEvent

说到发布这个事件,我们先看一个类图,我们知道此时的上下文是AnnotationConfigServletWebServerApplicationContext
在这里插入图片描述
  当调用publishEvent方法时,还是执行的父类:org.springframework.context.support.AbstractApplicationContextpublishEvent方法

	@Override
	public void publishEvent(ApplicationEvent event) {
		publishEvent(event, null);
	}
	
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		ApplicationEvent applicationEvent;
		// 判断event是否是ApplicationEvent的实例
		if (event instanceof ApplicationEvent) {
			// 是,转为ApplicationEvent
			applicationEvent = (ApplicationEvent) event;
		} else {
			// 不是,通过PayloadApplicationEvent来装饰为applicationEvent 
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}
		// 此时earlyApplicationEvents 为null
		if (this.earlyApplicationEvents != null) {
			// 早期事件不为null
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
			// 不为null,则通过SimpleApplicationEventMulticaster广播事件
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// 父上下文不为空
		if (this.parent != null) {
			// 父上下文是AbstractApplicationContext的实例
			if (this.parent instanceof AbstractApplicationContext) {
				// 转为AbstractApplicationContext,然后发布事件
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			} else {
				// 直接使用父上下文发布事件
				this.parent.publishEvent(event);
			}
		}
	}

  getApplicationEventMulticaster()实际获取到的就是SimpleApplicationEventMulticaster,通过它发布ApplicationStartedEvent事件,相信看我文章的都会非常的熟悉调用的过程。具体可以参考:Alian解读SpringBoot 2.6.0 源码(二):启动流程分析之监听器解析

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// executor默认为null
		Executor executor = getTaskExecutor();
		// 1、根据事件和事件类型获取监听器列表
		// 2、然后遍历监听器列表,分别调用监听器的方法
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			} else {
				// 调用
				invokeListener(listener, event);
			}
		}
	}
	
}

  最终 getApplicationListeners(event, type) 获取到的监听器有两个:

  • BackgroundPreinitializer
  • DelegatingApplicationListener

  实际上这两个监听器什么都没有做

4.2、AvailabilityChangeEvent

public class AvailabilityChangeEvent<S extends AvailabilityState> extends PayloadApplicationEvent<S> {
	public static <S extends AvailabilityState> void publish(ApplicationContext context, S state) {
		Assert.notNull(context, "Context must not be null");
		publish(context, context, state);
	}
	public static <S extends AvailabilityState> void publish(ApplicationEventPublisher publisher, Object source,
			S state) {
		Assert.notNull(publisher, "Publisher must not be null");
		publisher.publishEvent(new AvailabilityChangeEvent<>(source, state));
	}
}

  实际执行到这里,我们看一个简单类图:
在这里插入图片描述

  其实ApplicationEventPublisher 调用 publishEvent 方法就是AbstractApplicationContext执行publishEvent ,和上面上下文执行的过程是一样的,具体见上一章节,这里获取的监听器的结果如下:

  • DelegatingApplicationListener
  • ApplicationAvailabilityBean

  这里DelegatingApplicationListener是什么都没有做,而ApplicationAvailabilityBean就是给映射:
Map<Class<? extends AvailabilityState>, AvailabilityChangeEvent<?>> events = new HashMap<>();增加了一对映射
key为org.springframework.boot.availability.LivenessState,value为org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext]

五、执行所有 Runner 运行器

  此方法所在类的具体路径:org.springframework.boot.SpringApplication

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

  实际我这里什么都没有执行

六、发布ApplicationReadyEvent事件

  此方法所在类的具体路径:org.springframework.boot.SpringApplicationRunListeners

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;
	
	void ready(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.ready", (listener) -> listener.ready(context, timeTaken));
	}
	
	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, null);
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}
}

  此方法所在类的具体路径:org.springframework.boot.context.event.EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	@Override
	public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
		// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext
		// 初始化ApplicationReadyEvent
		// 通过应用上下文发布事件ApplicationReadyEvent
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
		// 通过AvailabilityChangeEvent发布事件
		AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
	}
}

  实际这里的调用过程和本文中之前发布应用上下文启动完成事件执行过程是一样的,就不多说了,应用上下文发布事件时,获取的监听器的结果如下:

监听器说明
SpringApplicationAdminMXBeanRegistrarready遍历设置为true
BackgroundPreinitializer等待预初始化任务执行完成(准备环境时执行),将preinitializationComplete减为0
DelegatingApplicationListener什么都没有做

  通过AvailabilityChangeEvent发布事件,获取的监听器的结果如下:

监听器说明
DelegatingApplicationListener什么都没有做
ApplicationAvailabilityBean参照本文中章节4.2的结果

结语

  到这里基本上整个springboot启动大致流程基本讲完了,我们还剩下后置处理器的调用,getBean方法的执行,以及主类的启动原理没有讲解了,期待您的关注。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值