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知识库 -> Spring Boot启动-4-应用监听器ApplicationListener -> 正文阅读

[Java知识库]Spring Boot启动-4-应用监听器ApplicationListener

前面我们讲了spring boot在run()方法的启动流程中,通过SpringApplicationRunListeners来手动执行不同阶段时的监听器回调,那里调用到的监听器都是ApplicationListener类型,这些监听器做的事情能解答我们使用spring boot的很多现象。

SpringBoot通过事件机制来完成启动流程执行,这样能够方便扩展。
在org.springframework.boot.SpringApplication类中有一个成员变量:

public class SpringApplication {
    ...
	private List<ApplicationListener<?>> listeners;
    ...
}

org.springframework.context.ApplicationListener
这里使用@FunctionalInterface注解来约束函数式接口定义,ApplicationListener是一个泛型接口,泛型用于接受不同类型的事件。需要了解的是,ApplicationListener的实现类非常多,足以说明这个事件监听机制使用的范围之广。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

可以看到,很多第三方的组件都去实现了ApplicationListener。

一、ApplicationListener的获取

这里的逻辑总结来说就是从classpath下META-INF/spring.factories文件里配置的ApplicationListener,具体的读取逻辑在方法:
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames()
具体逻辑有需要的时候可以再去研究。这里就读取了一部分ApplicationListener,是spring boot启动必须的监听器,同时需要注意的是,这个动作是在SpringApplication()构造方法里完成的,此时还没有执行run()方法。

public class 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();
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
    ...
}

二、ApplicationListener的调用

ApplicationListener注册后,调用的地方在run()方法里,通过SpringApplicationRunListeners ->?EventPublishingRunListener ->?SimpleApplicationEventMulticaster ->?ApplicationListener 这条链路调用到具体的监听器。

三、BootstrapApplicationListener

在org.springframework.context.ApplicationListener众多实现者有一个非常重要的实现就是
org.springframework.cloud.bootstrap.BootstrapApplicationListener
可以看到,ApplicationListener是定义在spring-context包中,属于经典上下文范畴,BootstrapApplicationListener是在spring-cloud-context中,属于cloud上线文范畴。
官方解释:
这个listener会prepare SpringApplication(例如填充他的Environment等),通过代理给在独立的bootstrap context里的ApplicationContextInitializer beans。bootstrap context 是SpringApplication从spring.factories中BootstrapConfiguration中创建的,从bootstrap.properties(or yml)而不是普通的application.properties(or yml)初始化的。

BootstrapApplicationListener执行后会创建一个id=bootstrap的上下文,这个上下文不是一个独立的父上下文,不是装载应用bean的上下文。

public class BootstrapApplicationListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {


	@Override
	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			return;
		}
		// don't listen to events in a bootstrap context
		if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
			return;
		}
		ConfigurableApplicationContext context = null;
		String configName = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
		for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
				.getInitializers()) {
			if (initializer instanceof ParentContextApplicationContextInitializer) {
				context = findBootstrapContext(
						(ParentContextApplicationContextInitializer) initializer,
						configName);
			}
		}
		if (context == null) {
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
			event.getSpringApplication()
					.addListeners(new CloseContextOnFailureApplicationListener(context));
		}

		apply(context, event.getSpringApplication(), environment);
	}


}

整个回调方法通过开关控制

spring.cloud.bootstrap.enabled

这里通过执行org.springframework.boot.SpringApplication#run(java.lang.String...)来生成ApplicationContext。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:07:52  更:2022-07-17 16:10:52 
 
开发: 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 15:49:14-

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