IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot 启动流程 -> 正文阅读

[Java知识库]SpringBoot 启动流程


?

使用的 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() 进行启动
        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) {
	//设置资源加载器 resourceLoader
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	//设置要加载的基础资源 primarySources
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	//推断应用类型
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	//设置应用上下文的初始化器 ApplicationContextInitializer
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	//设置应用监听器 ApplicationListener
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//推断主类
	this.mainApplicationClass = deduceMainApplicationClass();
}

主要是做一些初始化工作

?

设置基础资源 primarySources

//基础资源
private Set<Class<?>> primarySources;

基础资源是springboot应用中的一些核心类,通常是静态run()方法传入的引导类。所谓的加载基础资源,其实就是处理这些核心类,让这些核心类生效,比如解析引导类上的@SpringBootApplication、@MapperScan,使之生效。

?

推断应用类型 WebApplicationType

枚举类 WebApplicationType

public enum WebApplicationType {

	/**
	 * 不是web应用,不会使用内嵌的web容器来启动
	 */
	NONE,

	/**
	 * 是基于servlet的web应用,会使用内嵌的servlet容器启动
	 */
	SERVLET,

	/**
	 * 是基于reactive的web应用,会使用内嵌的reactive容器启动
	 */
	REACTIVE;


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


	/**
	 * 默认方式,根据classpath中是否有相应的核心类进行推断
	 */
	static WebApplicationType deduceFromClasspath() {
		//使用ClassUtils.isPresent()判断 classpath中是否有servlet或reactive的核心类,是否能用类加载器进行加载
		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;
	}


	/**
	 * 根据使用的上下文类型进行推断
	 * 比如servlet应用使用 ServletWebServerApplicationContext,reactive应用使用 ReactiveWebServerApplicationContext
	 */
	static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
		//判断是否是 WebApplicationContext 类或其子类
		if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.SERVLET;
		}
		//判断是否是 ReactiveWebApplicationContext 类或其子类
		if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.REACTIVE;
		}
		return WebApplicationType.NONE;
	}


	/**
	 * 判断2个接口|类是否是同一个或者是否是子类关系
	 * targe是字符串形式指定的全限定类名
	 */
	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 文件格式

#实质是 properties 文件
#key是接口,value指定该接口要使用的实现类,可指定多个实现类,都是使用全限定类名
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) {
	//获取类加载器:如果 this.resourceLoader 不为空则使用resourceLoader的类加载器,否则使用默认的类加载
	ClassLoader classLoader = getClassLoader();
	//加载、解析spring.factories,得到指定接口要使用的各个实现类的全限定类名
	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()

/**
 * 获取 spring.factories 中指定接口的要使用的各个实现类的全限定类名
 */
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	//loadSpringFactories()用于加载、解析所有的spring.factories文件,返回 Map<String, List<String>>,key是接口,value是该接口要使用的实现类列表
	//getOrDefault()用于从map中获取指定key的value,即指定接口对应的实现类。如果map中没有指定的key,或者对应的value为null,则返回指定的默认值
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}


/**
 * 获取 spring.factories 中指定接口的要使用的各个实现类的全限定类名
 */
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	//先从缓存中获取,有就直接返回,没有才加载、解析 spring.factories
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		//获取所有jar包中的 META-INF/spring.factories 文件路径
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
				
		// LinkedMultiValueMap,MultiValue 顾名思义 一个key可以对应多个value
		// 内部用 Map<K, List<V>> 存储数据,add(key,value)添加元素时,会把value放到对应的List中
		// 用于存储所有spring.factories文件的解析结果,一个键值对即一个接口的要使用的实现类,key是接口名,值List用于存储该接口要使用的实现类
		result = new LinkedMultiValueMap<>();
		
		//遍历所有的spring.factories文件,逐个解析
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			//解析为Properties
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			//遍历各个键值对
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				//key是接口名
				String factoryTypeName = ((String) entry.getKey()).trim();
				//value是包含多个实现类的字符串,切分为字符串数组进行遍历
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					//添加到 LinkedMultiValueMap 中
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		
		//把所有spring.factories文件的解析结果放到缓存中
		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) {
			//找到方法名为main的栈帧
			if ("main".equals(stackTraceElement.getMethodName())) {
				//把该栈帧对应的java类作为主类返回
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

?

SpringApplication的实例run()方法

整体流程

实例run()方法用于启动spring应用,创建并刷新一个新的应用上下文

/**
 * Run the Spring application, creating and refreshing a new ApplicationContext
 * 
 * @param args main()方法传递的参数
 * @return 一个已创建、启动的 ApplicationContext
 */
public ConfigurableApplicationContext run(String... args) {

	//创建并启动计时器
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	
	ConfigurableApplicationContext context = null;

	//配置系统属性 java.awt.headless
	configureHeadlessProperty();

	//创建运行监听器组
	//先根据 spring.factories 文件创建要使用的运行监听器 SpringApplicationRunListener 实例,再封装为运行监听器组
	//默认只有 EventPublishingRunListener 一个运行监听器,这个监听器会创建事件广播器 ApplicationEventMulticaster,把各个 ApplicationListener 实例绑定到事件广播器上,使ApplicationListener生效,回调方法的实现都是发布对应的启动事件
	SpringApplicationRunListeners listeners = getRunListeners(args);

	//执行运行监听器的starting()回调方法,发布应用启动事件 ApplicationStartingEvent
	listeners.starting();
	
	try {
	
		//构建应用参数
		//应用参数指的是传给main()方法的参数,其中可以包含 server.port 之类的应用预定义配置,也可以包含自定义的参数
		//args即传给main()方法的参数,这一步会对main()方法的参数进行封装
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		
		//准备环境
		//1、获取或创建环境(有就直接获取,没有就调用构造方法创建),2、配置环境
		//这一步会添加 profiles 激活的配置文件;触发SpringApplicationRunListener 的 environmentPrepared() 回调方法,发布环境已准备事件 ApplicationEnvironmentPreparedEvent
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		
		//配置 spring.beaninfo.ignore 属性
		configureIgnoreBeanInfo(environment);
		
		//打印banner
		Banner printedBanner = printBanner(environment);
		
		//创建应用上下文
		//会根据应用类型,通过反射调用对应的构造方法创建 ApplicationContext 的实例
		//这一步创建的是标准上下文(尚未进行配置),会转换为 ConfigurableApplicationContext 返回
		context = createApplicationContext();

		//准备上下文
		//主要是对上一步创建好的上下文进行配置、完善:1、对创建的上下文实例进行配置、初始化,2、配置内置的低级容器,3、加载所有BeanDefinition、完成bean的注册
		//这一步会加载所有的bean定义,执行运行监听器的 contextPrepared() 回调方法,发布上下文已初始化事件 ApplicationContextInitializedEvent
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);

		//刷新上下文
		//主要是调用高级容器的2个方法:1、registerShutdownHook() 向jvm注册ShutdownHook关闭钩子,在jvm终止时自动关闭上下文;2、refresh() 刷新上下文
		//这一步会实例化所有的单例bean(懒加载的除外),发布上下文已刷新事件 ContextRefreshedEvent,触发运行监听器的 contextLoaded() 回调方法
		refreshContext(context);

		//执行刷新后处理
		//这个是protected方法,默认为空实现,作为扩展点可被子类重写,以在上下文刷新之后做一些自定义的操作
		afterRefresh(context, applicationArguments);

		//终止计时器,打印启动花费的时长
		stopWatch.stop();
		if (this.logStartupInfo) {
			//日志示例:Started DemoApplication in 4.25 seconds (JVM running for 5.368)
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}

		//执行运行监听器的 started() 回调方法,发布应用已启动事件 ApplicationStartedEvent
		listeners.started(context);
		
		//执行 Runner 回调
		//这也是预留的一个扩展点,可以在应用启动后,根据main()参数做一些自定义的处理
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		// handleRunFailure()处理异常:
		// 1、如果是非正常退出(exitCode != 0),则发布应用退出事件 ExitCodeEvent,使用异常处理器 SpringBootExceptionHandler 记录退出码 exitCode;
		// 2、执行 SpringApplicationRunListener 的 failed() 回调方法;
		// 3、使用异常报告器 SpringBootExceptionReporter 打印、记录异常信息
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		//执行运行监听器的 running() 回调方法,发布应用就绪事件 ApplicationReadyEvent
		listeners.running(context);
	}
	catch (Throwable ex) {
		//和上面的 handleRunFailure() 差不多,只是传入的运行监听器组是null,不会执行 SpringApplicationRunListener 的 failed() 回调方法
		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();
		//把所有的应用事件监听器添加到事件广播器上
		// new SpringApplication() 创建 SpringApplication 实例时,只是创建了所有 ApplicationListener 的实例
		//这一步把 ApplicationListener 实例绑定到事件广播器上,ApplicationListener才能监听事件广播器发布的事件,才会生效
		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()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			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中对应的启动过程

//调用 SpringApplication 本身的 getRunListeners() 方法
SpringApplicationRunListeners listeners = getRunListeners(args);
//开启全部的 SpringApplicationRunListener
listeners.starting();
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	//先调用 getSpringFactoriesInstances(),根据spring.factories中的配置给要使用的SpringApplicationRunListener实现类创建实例
	//再包装为 SpringApplicationRunListeners,以便批量操作所有的 SpringApplicationRunListener 实例
	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) {
	//获取或创建环境,有现成的环境就直接使用,没有则调用构造方法new一个
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	//配置环境:上一步调用无参构造器创建了Environment实例,这一步对创建好的Environment实例进行配置
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	//绑定配置属性源、环境
	ConfigurationPropertySources.attach(environment);
	//至此环境已配置好,触发 SpringApplicationRunListener 的 environmentPrepared() 回调方法
	listeners.environmentPrepared(environment);
	//将配置好的环境绑定到 SpringApplication 上
	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) {
	// 处理 ConversionService 配置
	// ConversionService 是spring的一个线程安全的类型转换接口,convert(Object, Class) 可以将对象转换为指定类型,关键在于是线程安全的
	if (this.addConversionService) {
		ConversionService conversionService = ApplicationConversionService.getSharedInstance();
		environment.setConversionService((ConfigurableConversionService) conversionService);
	}
	//配置属性源
	configurePropertySources(environment, args);
	//配置profiles属性:这一步会把 spring.profiles 激活的配置文件添加到环境中
	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));
	}
	//配置命令行属性:如果给main()方法传递了命令行参数,则添加到Environment实例中
	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 {

	/**
	 * Print the banner to the specified print stream
	 * 
	 * @param environment the spring environment
	 * @param sourceClass the source class for the application
	 * @param out the output print stream
	 */
	void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

	/**
	 * banner的打印模式
	 */
	enum Mode {

		/**
		 * 禁用banner,不打印banner
		 */
		OFF,

		/**
		 * 输出流使用 System.out,标准输出,输出到控制台
		 */
		CONSOLE,

		/**
		 * 输出到日志文件中
		 */
		LOG

	}

}

?

SpringApplicaition 的 printBanner() 方法

//banner的默认打印模式是输出到控制台
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;

/**
 * 根据打印模式打印 banner
 */
private Banner printBanner(ConfigurableEnvironment environment) {
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
			: new DefaultResourceLoader(null);
	//通过 SpringApplicationBannerPrinter 类打印banner
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	//console模式的打印
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

?

SpringApplicationBannerPrinter 类,用于打印banner

class SpringApplicationBannerPrinter {
	
	//banner的配置项
	
	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" };
	
	/**
	 * 默认的banner。SpringBootBanner是banner的默认实现
	 */
	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) {
		//...
	}

	/**
	 * 打印文本形式的banner
	 */
	private Banner getTextBanner(Environment environment) {
		//...
	}

	/**
	 * 打印图片形式的banner
	 */
	private Banner getImageBanner(Environment environment) {
		//...
	}

	/**
	 * 创建文本形式的banner字符串
	 */
	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);
	//应用初始化器:执行各个应用上下文初始化器(ApplicationContextInitializer)的 shaninitialize() 方法,对上下文进行初始化
	applyInitializers(context);
	//至此上下文准备完毕,执行各个 SpringApplicationRunListener 的 contextPrepared() 回调方法
	listeners.contextPrepared(context);
	
	//如果允许打印启动信息,则
	if (this.logStartupInfo) {
		//打印应用正在启动的信息,示例:Starting DemoApplication on DESKTOP-E74978P with PID 49824
		logStartupInfo(context.getParent() == null);
		//打印 spring.profiles 激活的配置,示例:The following profiles are active: test
		//没有设置激活的配置则打印默认配置:No active profile set, falling back to default profiles: default
		//spring.profiles除了include、active之外,还有一个default属性用于指定默认配置文件
		logStartupProfileInfo(context);
	}
	
	//获取内置的低级容器,以对内置的低级容器进行一些配置
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	//注册一些特殊的单例bean:应用参数、banner
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	//配置是否允许bean定义覆盖
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	//配置是否允许bean的懒加载。默认false,高级容器启动时就实例化所有单例
	if (this.lazyInitialization) {
		//如果允许bean的懒加载,则给内置的低级容器添加懒加载对应的后置处理器
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	
	//获取所有的资源,所有的资源指的是 this.primarySources、this.sources 2个属性指定的资源
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	//加载所有的资源:使用 BeanDefinitionLoader 加载所有的bean定义,完成bean的注册
	load(context, sources.toArray(new Object[0]));
	
	//执行 SpringApplicationRunListener 的 contextLoaded() 回调方法
	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<>();
	//从容器中获取runner
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	//对runner进行排序。就是说支持多个runner,可以用 @Order 或实现Order接口指定这些runner的执行顺序
	AnnotationAwareOrderComparator.sort(runners);
	//遍历执行这些runner的run()方法。注意传入的args是 ApplicationArguments 类型
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		// 对于 CommandLineRunner,会先 args.getSourceArgs() 获取 String... 类型的参数
		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的启动过程

//new SpringApplication()创建SpringApplication实例阶段
//会根据 spring.factories 创建所有的 ApplicationListener 实例
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));


//实例run()方法启动应用阶段
//会创建事件广播器 ApplicationEventMulticaster,并将各个ApplicationListener实例绑定到事件广播器上
SpringApplicationRunListeners listeners = getRunListeners(args);


//调用高级容器的 refresh() 方法刷新上下文
refreshContext()

?
高级容器 refresh() 刷新阶段

//预刷新

//初始化事件广播器
//会实例化ApplicationEventMulticaster,并注册到单例注册表(一级缓存)中,以提供应用事件多播的功能
initApplicationEventMulticaster();

//注册应用监听器 ApplicationListener,将应用监听器和事件广播器绑定,并广播预刷新阶段存储的早期事件
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;
	//创建了事件广播器 ApplicationEventMulticaster,此时容器|上下文尚未创建,事件广播器也是以成员变量的形式保存在运行监听器 EventPublishingRunListener 的实例中
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	//获取SpringApplication实例保存的所有ApplicationListener实例,绑定到这个事件广播器上
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}


//这个回调方法会在 prepareContext() 准备上下文过程中调用
//此时上下文已创建、初始化,所有的bean定义已加载,但尚未调用 refresh() 刷新上下文,bean实例尚未创建
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
	//其它回调方法都是直接发布事件,这个回调方法除了发布事件还会把 ApplicationListener 实例传给创建好的山下文 ApplicationContext
	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() 刷新阶段

// 初始化事件广播器
// 实质是调用 beanFactory.getBean() 从低级容器中获取对应的实例(会创建实例放到容器中),把实例赋给 AbstractApplicationContext 的对应的成员变量
// this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
initApplicationEventMulticaster();


//注册应用监听器 ApplicationListener
//
registerListeners();
protected void registerListeners() {
	//注册监听器,指的是 把监听器实例添加到事件广播器上
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	//将 beanFactory().getBeanNamesForType() 从容器中获取应用监听器的beanName
	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就是这么神奇,刷一下就有了。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-16 19:30:54  更:2021-10-16 19:30:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:32:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码