?
使用的 springboot 源码版本 2.3.12.RELEASE,这是2.3.x系列的最后一个版本。
springboot应用的启动方式很多,可以在引导类中配置 ConfigurableApplicationContext,可以继承 SpringBootServletInitializer 重写SpringApplication的配置,可以用 SpringApplicationBuilder 来构建SpringApplication…不同的启动方式、不同版本的源码,启动过程、调用的一些方法有所差别,整体过程大同小异,此处以默认的启动方式进行讲述。
文中涉及到的Banner、SpringApplicationRunListener、Runner、StopWatch 的使用方式,可参考我的另一篇博文
https://blog.csdn.net/chy_18883701161/article/details/120753879
?
SpringApplication的静态run()方法
引导类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
?
实质是调用 SpringApplication 本身的另一个重载的静态 run() 方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这个方法做了2件事
- 调用 SpringApplication 的构造方法创建 SpringApplication 实例
- 调用 SpringApplication 实例的run()方法进行启动
这个实例 run() 方法是启动的核心|主要方法
?
springboot的启动思路
- 核心类是 SpringApplication ,核心方法是 SpringApplication 的实例 run() 方法
- 调用 SpringApplication 的静态run()方法进行启动,由静态 run() 方法创建 SpringApplication 实例,调用实例的 run() 方法进行启动。
?
SpringApplication的构造方法
整体流程
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
主要是做一些初始化工作
?
设置基础资源 primarySources
private Set<Class<?>> primarySources;
基础资源是springboot应用中的一些核心类,通常是静态run()方法传入的引导类。所谓的加载基础资源,其实就是处理这些核心类,让这些核心类生效,比如解析引导类上的@SpringBootApplication、@MapperScan,使之生效。
?
推断应用类型 WebApplicationType
枚举类 WebApplicationType
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
这个枚举类主要
- 定义了3种应用类型:none、servlet、reactive
- 提供了2种推断应用类型的方法:deduceFromClasspath 根据classpath中是否对应的核心类进行推断(默认方式)、deduceFromApplicationContext 根据使用的上下文类型进行推断
?
设置应用上下文初始化器、应用监听器
spring.factories 文件格式
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
?
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
都是调用 getSpringFactoriesInstances(),根据 spring.factories 文件中的配置获取指定接口(此处是ApplicationContextInitializer接口、ApplicationListener接口)要使用的实现类,给这些实现类创建实例。
创建的 pplicationContextInitializer、ApplicationListener 实例会赋给 SpringApplication 对应的成员变量保存,以完成这些成员变量的初始化。
?
SpringApplication 的实例方法 getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
?
SpringFactoriesLoader 的静态方法 loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
?
显然,第一次调用 getSpringFactoriesInstances() 时,就加载、解析了所有的 spring.factories 文件,把解析结果放到了缓存中,但只实例化了指定接口要使用的实现类。
后续再调用 getSpringFactoriesInstances() 时,直接从缓存中获取解析结果(map),从中获取指定接口的实现类,对其进行实例化,没有再加载、解析spring.factories文件。
?
推断主类 deduceMainApplicationClass()
SpringApplication 的实例方法 deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
}
return null;
}
?
SpringApplication的实例run()方法
整体流程
实例run()方法用于启动spring应用,创建并刷新一个新的应用上下文
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
?
运行监听器 SpringApplicationRunListener
RunListener 运行监听器,准确来说,应该叫做 启动监听器。 ?
SpringApplicationRunListener 接口
这个监听器用于监听spring应用的启动事件,即监听 SpringApplication 的实例 run() 方法的执行过程,提供了一系列回调方法,可在run()方法执行 | 启动过程的特定阶段做一些额外操作
public interface SpringApplicationRunListener {
default void starting() {
}
default void environmentPrepared(ConfigurableEnvironment environment) {
}
default void contextPrepared(ConfigurableApplicationContext context) {
}
default void contextLoaded(ConfigurableApplicationContext context) {
}
default void started(ConfigurableApplicationContext context) {
}
default void running(ConfigurableApplicationContext context) {
}
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
注意:这个接口没有继承 ApplicationListener,不是 ApplicationListener 系列的。 ?
ApplicationListener 应用监听器、SpringApplicationRunListener 运行监听器,都是监听器 Listener,但区别很大
- 来源不同:ApplicationListener是spring提供的,用于构建spring的事件驱动模型;SpringApplicationRunListener是springboot提供的,用于在应用启动的特定阶段做一些额外操作
- 监听的对象不同: ApplicationListener是监听事件广播器发布的应用事件,SpringApplicationRunListener是监听应用的启动过程(SpringApplication的实例run方法的执行过程)
- 触发机制不同:ApplicationListener要监听到事件广播器发布的特定事件时,才会触发回调方法;SpringApplicationRunListener是在应用启动的特定阶段执行方法回调,应用启动到了特定阶段就会执行,跟事件发布没有必然联系
?
EventPublishingRunListener 实现类
SpringApplicationRunListener 接口提供的都是默认方法,只提供了空实现,springboot给这个接口只提供了一个实现类 EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
context.publishEvent(event);
}
else {
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
}
}
主要关注2点
- 在构造方法中创建了应用事件广播器 ApplicationEventMulticaster,将应用事件监听器 ApplicationListener 绑定到事件广播器上,使各个应用事件监听器生效
- 回调方法都是通过事件广播器,发布相应的事件
?
SpringApplicationRunListeners 运行监听器组
SpringApplicationRunListener 的复数形式,这个类用于封装多个 SpringApplicationRunListener,提供多个 SpringApplicationRunListener 的批量操作
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
}
}
?
springboot中对应的启动过程
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
?
springboot本身只给 SpringApplicationRunListener 提供了一个实现类,springboot自身的 spring.factories 中也只启用了这个实现
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
为什么还要打包为 SpringApplicationRunListeners 进行批量操作?就一个运行监听器,似乎没有打包的必要。
因为开发者可能需要在应用启动的特定阶段做一些自定义操作,要实现 SpringApplicationRunListener 重写回调方法,这样就会存在多个 SpringApplicationRunListener。
?
准备环境 prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
?
getOrCreateEnvironment() 获取或创建环境
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 = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
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));
}
}
}
?
打印Banner printBanner()
Banner接口
@FunctionalInterface
public interface Banner {
void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);
enum Mode {
OFF,
CONSOLE,
LOG
}
}
?
SpringApplicaition 的 printBanner() 方法
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
?
SpringApplicationBannerPrinter 类,用于打印banner
class SpringApplicationBannerPrinter {
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
private final ResourceLoader resourceLoader;
private final Banner fallbackBanner;
SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
}
Banner print(Environment environment, Class<?> sourceClass, Log logger) {
}
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
}
private Banner getBanner(Environment environment) {
}
private Banner getTextBanner(Environment environment) {
}
private Banner getImageBanner(Environment environment) {
}
private String createStringFromBanner(Banner banner, Environment environment, Class<?> mainApplicationClass)
throws UnsupportedEncodingException {
}
private static class Banners implements Banner {
}
private static class PrintedBanner implements Banner {
}
}
?
创建应用上下文 createApplicationContext()
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
protected ConfigurableApplicationContext createApplicationContext() {
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);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
?
准备上下文 prepareContext()
上一步创建应用上下文,只是通过反射调用应用上下文的构造方法创建实例,尚未对上下文实例进行配置。
这一步用于配置创建好的上下文实例,主要包括
- 配置、初始化应用上下文:给应用上下文设置环境;执行应用上下文的后置处理,给一些尚未填充的属性设置值;执行应用上下文初始化器(ApplicationContextInitializer),对应用上下文进行初始化
- 打印应用的启动信息
- 配置内置的低级容器 (至此,高级容器、内置的低级容器都已创建、配置完毕)
- 加载所有的 BeanDefinition,完成bean的注册
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
?
刷新上下文 refreshContext()
实质是调用高级容器的2个方法
- registerShutdownHook():向jvm注册ShutdownHook关闭钩子,在jvm终止时自动关闭应用上下文
- refresh():刷新上下文
?
refresh() 执行的具体过程可参考的我的另一篇博文
https://blog.csdn.net/chy_18883701161/article/details/120550053
?
callRunners() 执行Runner回调
2种Runner
runner有2种:CommandLineRunner、ApplicationRunner
@FunctionalInterface
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
都是函数式接口、只有一个run()方法,区别是参数类型不同
- CommandLineRunner:顾名思义,基于命令行,使用的使用 String… 接收命令行的多个参数
- ApplicationRunner:使用的是应用参数 ApplicationArguments
其实使用的都是 main() 方法的参数,ApplicationArguments 只是对main()参数的封装。
?
springboot启动过程中调用的方法
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);
}
}
}
通过2个runner接口,可以在应用启动后根据 main() 方法传入的参数做一些额外处理,当然,不使用main()参数、直接做一些与mian()参数无关的操作也行。
?
整体流程图
?
springboot启动过程中的扩展点
springboot在启动过程中主要预留个3个扩展点
- 预留的 afterRefresh() 方法:在 refresh() 刷上下文后进行调用
- runner 回调:在应用启动后调用,方法参数和 afterRefresh() 差不多,都是应用上下文 context、应用参数 applicationArguments。
- 自定义运行监听器 SpringApplicationRunListener:可以在启动过程中的多个阶段做一些额外处理。
像 afterRefresh() 这种 protected 修饰的空方法,基本都是预留的扩展点,留给子类重写的,不想让子类重写的都是定义为 private。
不熟悉springboot的同学,使用 afterRefresh() 很容易踩坑,能用另外2种方式就用另外2种方式。
?
附录
Runner、运行监听器、springboot的启动日志分析
我在自定义运行监听器的各个回调方法中打印出了回调方法名称,并标注了对应的启动阶段、触发的回调方法、回调方法中发布的事件
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe" ...
# listeners.starting()中,starting() ,发布应用启动事件 ApplicationStartingEvent
starting...
# prepareEnvironment() 准备环境,environmentPrepared() ,发布环境已准备事件 ApplicationEnvironmentPreparedEvent
environmentPrepared...
# printBanner() 打印banner。打印的默认banner中会包含springboot的版本
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.12.RELEASE)
# prepareContext() 准备上下文,contextPrepared() ,发布上下文已初始化事件 ApplicationContextInitializedEvent
contextPrepared...
# prepareContext()打印的应用启动信息
2021-10-15 15:20:08.098 INFO 50880 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on DESKTOP-E74978P with PID 50880 (C:\Users\chy\Desktop\demo\target\classes started by chy in C:\Users\chy\Desktop\demo)
# prepareContext()打印的profile激活的配置
2021-10-15 15:20:08.109 INFO 50880 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
# refreshContext() 刷新上下文,contextLoaded() ,发布上下文已刷新事件 ContextRefreshedEvent
contextLoaded...
#初始化内置tmocat要使用的端口
2021-10-15 15:20:10.434 INFO 50880 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
#开始启动内置tomcat
2021-10-15 15:20:10.453 INFO 50880 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
#开启tomcat的servlet引擎,打印信息中包含了内置tomcat的版本
2021-10-15 15:20:10.453 INFO 50880 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46]
#初始化tomcat本身的应用上下文,打印信息中包含了映射的域名、应用映射的根路径
2021-10-15 15:20:10.604 INFO 50880 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/mall] : Initializing Spring embedded WebApplicationContext
#tomcat本身的根上下文初始化完成,打印花费的时间
2021-10-15 15:20:10.605 INFO 50880 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2327 ms
#初始化tomcat的执行器
2021-10-15 15:20:11.084 INFO 50880 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
#内置tomcat启动完成,打印tomcat使用的端口、应用映射的根路径
2021-10-15 15:20:11.643 INFO 50880 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/mall'
#至此,应用启动完成,打印启动应用总共花费的时间
2021-10-15 15:20:11.678 INFO 50880 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 4.559 seconds (JVM running for 5.832)
# listeners.started() 执行运行监听器组的 started() 方法,started(),发布应用已启动事件 ApplicationStartedEvent
started...
# callRunners() 执行Runner 回调
ApplicationRunner...
CommandLineRunner...
# listeners.running() 执行运行监听器组的 running() 方法,running(),发布应用就绪事件 ApplicationReadyEvent
running...
#初始化springmvc的核心组件 DispatcherServlet,会作为bean放到容器中,已经打印出了bean的名称 'dispatcherServlet'
2021-10-15 15:20:12.917 INFO 50880 --- [(2)-10.10.10.84] o.a.c.c.C.[Tomcat].[localhost].[/mall] : Initializing Spring DispatcherServlet 'dispatcherServlet'
#初始化DispatcherServlet这个servlet,把DispatcherServlet配置为servlet
2021-10-15 15:20:12.917 INFO 50880 --- [(2)-10.10.10.84] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
#DispatcherServlet初始化完毕,打印初始化DispatcherServlet花费的时间
2021-10-15 15:20:12.929 INFO 50880 --- [(2)-10.10.10.84] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
可以看到,在应用启动完成后会初始化 springmvc | spring webflux 的核心组件。
如果要查看更详细的启动信息,可以将 debug 设置为 true,或者将日志级别设置为 debug。
?
关于应用监听器、事件广播器的思考
问题
springboot的启动过程
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
SpringApplicationRunListeners listeners = getRunListeners(args);
refreshContext()
? 高级容器 refresh() 刷新阶段
initApplicationEventMulticaster();
registerListeners();
也创建了事件广播器、将应用监听器和事件广播器绑定,这不会矛盾、重复吗?
?
答案
不矛盾,也不重复。 ?
1、new SpringApplication() 创建SpringApplication实例阶段
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
创建了所有ApplicationListener的实例,此时容器|上下文尚未创建,这些ApplicationListener实例是以成员变量的形式保存在SpringApplication实例中的
private List<ApplicationListener<?>> listeners;
?
2、实例 run() 方法启动应用阶段
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
?
3、高级容器的 refresh() 刷新阶段
initApplicationEventMulticaster();
registerListeners();
protected void registerListeners() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
abstractApplicationContext.publishEvent();
?
关于 refresh() 方法的名称
- 粉刷墙壁,材料都准备好了,在墙上刷一下砂灰就出来了
- 魔术师双手拿着一块布,遮住一个准备好的盘子,突然移开布(刷一下),变出2个苹果
- 法海让小和尚准备好了一个大缸,老和尚大袖子遮住缸,手一挥(刷一下),变出了一缸子的水
- 容器已经创建、初始化,所有bean定义都已解析、加载,刷一下(调用refresh方法),容器中变出了所有单例bean(懒加载除外)的实例
都是准备好环境、材料,刷一下,得到产物,现在是不是觉得 refresh 刷新 这个方法名很形象?spring就是这么神奇,刷一下就有了。
|