深入浅出SpringBoot源码——监听器与事件机制

一篇文章读懂SimpleApplicationEventMulticaster Spring 框架中的事件机制是观察者设计模式的一个典型实现,它基于发布-订阅模式(Publish-Subscribe)。在 Spring 中,事件是由事件发布者(ApplicationEventPublisher)发布的,而事件监听器(ApplicationListener)则是观察者,负责处理特定类型的事件。事件是应用程序中可能发生的事情,它通常是一个继承自的类。开发人员可以定义自己的事件类,以便在应用程序中传递关键信息。接口是事件发布者的标准接口,它定义了发布事件的方法。 阅读详情

前言

先看下Spring官方文档对于事件以及监听器的解释与说明。
监听器官方说明

在这里插入图片描述
总结起来就是几点:

  1. 除了通常的Spring框架自带的事件例如:ContextRefreshedEvent,SpringApplication还会发送一些额外的事件。
  2. 对于事件的监听,需要通过监听器来实现。在SpringBoot中,监听器可以通过三种方式来注册,
    ① 通过SpringApplication.addListeners(…)
    ② 通过SpringApplicationBuilder.listeners(…)
    ③ 通过注册在META-INF/spring.factories中,示例为org.springframework.context.ApplicationListener=com.example.project.MyListener
  3. 监听器不能以Bean的形式注册进SpringIOC容器中,因为监听器是在ApplicationContext上下文创建成功之前调用的。
  4. Spring应用程序的内置事件会以以下顺序发送:
    ① ApplicationStartingEvent
    ② ApplicationEnvironmentPreparedEvent
    ③ ApplicationContextInitializedEvent
    ④ ApplicationPreparedEvent
    ⑤ ApplicationStartedEvent
    ⑥ AvailabilityChangeEvent
    ⑦ ApplicationReadyEvent
    ⑧ AvailabilityChangeEvent
    ⑨ ApplicationFailedEvent
    除了上面这些绑定在SpringApplication上的事件外,还有ContextRefreshedEvent和WebServerInitializedEvent、ServletWebServerInitializedEvent、ReactiveWebServerInitializedEvent等事件在ApplicationPreparedEvent和ApplicationStartedEvent事件之间发布。

在介绍完SpringBoot的官方文档后,下面看看SpringBoot源码中监听器是如何实现的。

正文

1. SpringBoot通过factories注册的监听器

在前面已经讲解过了SpringFactoriesLoader加载spring.factories的原理机制,大家都清楚了SpringFactoriesLoader会加载spring.factories中注册的初始化器、监听器、后置处理器等组件,在SpringApplication的构造方法中会通过JDK的反射工具实例化这些组件。
在这里插入图片描述
从源码中可以看到,SpringBoot在SpringApplication的构造方法中,会先获取ApplicationListener类型的监听器,并存进SpringApplication对象中,用于后续操作的调用。

下面看看ApplicationListener接口定义信息

/**
 * Interface to be implemented by application event listeners.
 *
 * <p>Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an {@code ApplicationListener} can generically declare
 * the event type that it is interested in. When registered with a Spring
 * {@code ApplicationContext}, events will be filtered accordingly, with the
 * listener getting invoked for matching event objects only.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific {@code ApplicationEvent} subclass to listen to
 * @see org.springframework.context.ApplicationEvent
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.event.EventListener
 */
@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接口是用于定义Spring应用程序监听器的接口,是基于Observer(观察者)设计模式的标准接口。从Spring3.0开始,ApplicationListener可以声明感兴趣的事件类型。

注册完spring.factories配置文件中的监听器后,SpringBoot框架是什么时候开始获取监听器然后调用监听器的监听的事件呢?下面来看SpringApplication#run方法。

重点需要关注下ApplicationListener#onApplicationEvent方法,这个方法是广播器播放监听器时调用的方法,详情会在下文中讲解。

SpringApplication#run
在这里插入图片描述

SpringApplication#getRunListeners
在这里插入图片描述
这个方法就是通过SpringFactoriesLoader去加载spring.factories配置文件中的文件指定的SpringApplicationRunListener类类型,这里获取的就是EventPublishingRunListener:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

在这里插入图片描述
EventPublishingRunListener有什么作用?

1.1 EventPublishingRunListener

下面先看下EventPublishingRunListener源码

/**
 * SpringApplicationRunListener 是用于发布 SpringApplicationEvent的。
 * SpringApplicationRunListener通过内部的ApplicationEventMulticaster在容器刷新之前来触发事件。
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @author Andy Wilkinson
 * @author Artsiom Yudovin
 * @since 1.0.0
 */
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();
		// 获取SpringApplicaiton中实例化的事件监听器,添加进事件播放器中。listeners集合最终会存于AbstractApplicationEventMulticaster类中一内部类的一个Set集合中。
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	/**
	 * 通过事件播放器播放ApplicationStartingEvent事件
	 */
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	/**
	 * 通过事件播放器播放ApplicationEnvironmentPreparedEvent事件
	 */
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	/**
	 * 通过事件播放器播放ApplicationContextInitializedEvent事件
	 */
	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	/**
	 * 通过事件播放器播放ApplicationPreparedEvent事件
	 */
	@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));
	}

	/**
	 * 通过事件播放器播放ApplicationStartedEvent事件
	 */
	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

	/**
	 * 通过事件播放器播放ApplicationReadyEvent事件
	 */
	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

	/**
	 * 通过事件播放器播放ApplicationFailedEvent事件
	 */
	@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 {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}

EventPublishingRunListener实现了SpringApplicationRunListener接口,该接口定义了用于监听SpringApplication生命周期的一系列接口方法。

public interface SpringApplicationRunListener {

	/**
	 * 在首次启动run方法时立即调用。 可用于非常早的初始化。
	 */
	default void starting() {
	}

	/**
	 * 在准备SpringIOC容器之后,创建好SpringIOC容器之前调用。
	 */
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	/**
	 * 在创建和准备SpringIOC容器之后,但在加载源之前调用。
	 */
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	/**
	 * 在SpringIOC容器加载完之后但是在其刷新之前调用。
	 */
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	/**
	 * 上下文已刷新,应用程序已启动,但CommandLineRunner 和ApplicationRunner 尚未被调用。
	 */
	default void started(ConfigurableApplicationContext context) {
	}

	/**
	 * 在刷新应用程序上下文并已调用所有CommandLineRunner和ApplicationRunner后,在run方法完成之前立即调用。 
	 */
	default void running(ConfigurableApplicationContext context) {
	}

	/**
	 * 运行应用程序时发生故障时调用。
	 */
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}
1.2 Event事件

分析完EventPublishingRunListener之后,我们看下在事件Event。在SpringBoot中,EventObject定义了系统事件的顶级父类,其类定义中定义了一个成员变量source,用于存储事件的初始源头,对于SpringBoot的系统事件,源头就表示的是SpringIOC容器。
在这里插入图片描述
可以看到SpringApplicationEvent定义的系统事件基本都围绕着SpringBoot框架的启动生命周期。
在这里插入图片描述
在这里插入图片描述

而在EventPublishingRunListener中播放的事件,就是SpringBoot系统事件发送顺序,顺序如下:
在这里插入图片描述

2. SpringBoot中的事件广播器

在EventPublishingRunListener内部中,有一个名为SimpleApplicationEventMulticaster的成员变量,它是一种广播器,专门用于管理SpringBoot中的监听器,并广播指定的类型事件。SpringBoot定义了一个事件广播器接口,用于抽象播放器的接口行为,该接口名称为:ApplicationEventMulticaster,看下它的实现类的层次结构:
在这里插入图片描述
因此可以知道,在SpringBoot中核心广播器就是SimpleApplicationEventMulticaster,所有管理监听器播放事件的工作都由SimpleApplicationEventMulticaster来完成。下面先来看下接口ApplicationEventMulticaster源码及注释:

/**
 * ApplicationEventMulticaster接口的实现类用于管理多个ApplicationListener监听器,并对事件进行广播
 *
 * ApplicationEventMulticaster实际上就是作为Spring真正播放事件的一个代理。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @see ApplicationListener
 */
public interface ApplicationEventMulticaster {

	/**
	 * 添加一个监听器用于触发所有事件
	 */
	void addApplicationListener(ApplicationListener<?> listener);

	/**
	 * 添加一个监听器的bean用于触发所有事件
	 */
	void addApplicationListenerBean(String listenerBeanName);

	/**
	 * 从通知列表中移除一个监听器
	 */
	void removeApplicationListener(ApplicationListener<?> listener);

	/**
	 * 从通知列表中移除一个监听器的bean
	 */
	void removeApplicationListenerBean(String listenerBeanName);

	/**
	 * 移除所有注册在广播器上的监听器。
	 */
	void removeAllListeners();

	/**
	 * 在适当的监听器上播放ApplicationEvent事件
	 */
	void multicastEvent(ApplicationEvent event);

	/**
	 * 在适当的监听器上播放ApplicationEvent事件
	 * 可以通过eventType来过滤需要播放的事件类型。
	 */
	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}

接着看AbstractApplicationEventMulticaster抽象类源码,由于AbstractApplicationEventMulticaster方法较多,就挑几个核心方法进行讲解并学习。

AbstractApplicationEventMulticaster类注释可以总结成以下几点:

  1. ApplicationEventMulticaster接口的抽象实现,提供了基本的监听器注册功能。
  2. 默认情况下,框架不允许同一监听器有多个实例,因为监听器会被存放到set集合中。
  3. 通常ApplicationEventMulticaster接口的multicastEvent方法是留给子类SimpleApplicationEventMulticaster实现,它通过multicastEvent方法将所有事件广播到所有已注册的监听器,并在调用线程中播放事件。

下面是AbstractApplicationEventMulticaster类的定义以及成员变量:
在这里插入图片描述
AbstractApplicationEventMulticaster不仅实现了ApplicationEventMulticaster,还实现了BeanClassLoaderAware以及BeanFactoryAware。在SpringBoot中,实现了XXAware,都会获得注入XX的能力,比如常见的ApplicationContextAware,就是拥有注入ApplicationContext的能力;所以AbstractApplicationEventMulticaster即拥有了注入BeanClass和BeanFactory的能力。

成员变量中,defaultRetriever是一个内部类,作为一个存储监听器实例的数据结构,其底层通过Set集合来存储监听器。retrieverCache是一个ConcurrentHashMap实现的内部缓存,用于在下次获取监听器时直接能从缓存中获取。retrievalMutex是一个Object对象,用作对象锁。

AbstractApplicationEventMulticaster已经把监听器存储好了,就等着广播器进行事件广播,而广播的方法就是视SimpleApplicationEventMulticaster#multicastEvent方法。

@Override
	public void multicastEvent(ApplicationEvent event) {
		// 广播事件
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		// 获取监听器集合
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				// 如果有设置线程池,则调用线程池中的线程来调用监听
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

下面看下广播器是如何获取到监听器集合的。

2.1 AbstractApplicationEventMulticaster#getApplicationListeners

既然监听器存放在了播放器里,那么播放器肯定会提供一个获取监听器的方法,那么这个方法就是getApplicationListeners。
AbstractApplicationEventMulticaster#getApplicationListeners

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {

		// 获取事件的源,也就是SpringApplication对象。
		Object source = event.getSource();
		// 获取SpringApplication类对象
		Class<?> sourceType = (source != null ? source.getClass() : null);
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// 从缓存中获取监听器
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}

		if (this.beanClassLoader == null ||
				(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
			// 异步地构建以及缓存ListenerRetriever
			synchronized (this.retrievalMutex) {
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
					// 从缓存中获取监听器
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				// 获取监听器集合
				Collection<ApplicationListener<?>> listeners =
						retrieveApplicationListeners(eventType, sourceType, retriever);
				// 将存放监听器的retriever存入ConcurrentHashMap中
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		}
		else {
			// 没有ListenerRetriever 缓存就没必要进行异步获取
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}

看下ListenerRetriever#retrieveApplicationListeners源码
在这里插入图片描述
类ListenerRetriever的成员变量applicationListeners实际存储的就是spring.factories文件中注册的监听器。那问题来了,spring.factories文件里注册的监听器是什么时候注册进applicationListeners集合中的?

目光得回到EventPublishingRunListener的构造方法中,
在这里插入图片描述
在这里插入图片描述
而EventPublishingRunListener构造方法是通过反射来方法来调用的。
在这里插入图片描述
下图为SpringBoot如何将监听器添加进EventPublishingRunListener中的简易流程图。
在这里插入图片描述
除此之外,还有一个重要方法AbstractApplicationEventMulticaster#supportsEvent,该方法有两个重载方法

supportsEvent(
			ConfigurableBeanFactory beanFactory, String listenerBeanName, ResolvableType eventType)
supportsEvent(Class<?> listenerType, ResolvableType eventType)

二者区别在于第一个supportsEvent方法需要传入beanFactory,用作于获取bean的申明类型。在尝试实例化bean定义的侦听器之前,请先检查其通用声明的事件类型,以对其进行早期过滤。
在这里插入图片描述
这里有一个比较陌生的类,就是ResolvableType,这是啥类?就如其名字,一个ResolvableType对象就代表着一种可解决的java类型,我们可以通过ResolvableType对象可以获取类型携带的信息 ,包括父类型、接口类型、泛型类型等。

在这里插入图片描述

3. SpringBoot中的事件广播器如何播放监听器中的事件?

上文已经分别讲解了事件、监听器、广播器,那么三者之间的调用关系又是怎样的关系呢?说的再好不如debug看下调用栈调用流程来的一清二楚。断点就打在SimpleApplicationEventMulticaster#multicastEvent方法中。

在这里插入图片描述在这里插入图片描述
由于此处EVENT_TYPES类型包含了ApplicationStartingEvents.class,所以是符合类型的事件,因此会把LogginApplicationListener存放进Set集合中。
最终可以看到,对于starting方法,拥有感兴趣的ApplicationStartingEvents事件类型的监听器有下图的四种
在这里插入图片描述
最终,会在SimpleApplicationEventMulticaster#doInvokeListener中进行监听器的onApplicationEvent调用,最终播放监听器。
在这里插入图片描述
在SimpleApplicationEventMulticaster#multicastEvent方法中,由getApplicationListeners方法返回了拥有感兴趣事件类型的监听器实例集合,上图已经证实,在SpringBoot中对ApplicationStartingEvent事件类型感兴趣的监听器有四种。
在这里插入图片描述
因此SpringBoot的广播器会依次播放这四种监听器的onApplicationEvent方法。

从事件调用发起到最终监听器播放事件,可以总结为下图的流程:
在这里插入图片描述

那么经过debug分析,事件、监听器、广播器之间的关系就已经变得明朗起来了。

3.1 事件、监听器、广播器之间的关系

对于广播器SimpleApplicationEventMulticaster,其作用就是存储监听器、根据具体的事件筛选出感兴趣的监听器,然后播放监听器;而监听器的onApplicationEvent方法会传入事件,来进行具体的播放逻辑。当然,广播器是作为EventPublishingRunListener的成员变量。因而在SpringApplication的生命周期中,通过广播器会分别调用对应感兴趣事件的监听器。

4. 模仿SpringBoot,实现自定义的事件与监听器

首先,定义一个天气事件抽象类

public abstract class WeatherEvent {
    public abstract String getWeather();
}

定义两个天气事件

public class RainEvent extends WeatherEvent{
    @Override
    public String getWeather() {
        return "rain...";
    }
}
public class SnowEvent extends WeatherEvent{
    @Override
    public String getWeather() {
        return "snow...";
    }
}

接着定义一个事件监听器

public interface WeatherListener {
	// 类似于SpringBoot监听器的onApplicationEvent方法
    void onWeatherEvent(WeatherEvent event);
}

有了监听器接口,那么就要定义实现类

@Component
public class RainListener implements WeatherListener{
    @Override
    public void onWeatherEvent(WeatherEvent event) {
        if (event instanceof RainEvent) {
            System.out.println("hello " + event.getWeather());
        }
    }
}
@Component
public class SnowListener implements WeatherListener {
    @Override
    public void onWeatherEvent(WeatherEvent event) {
        if (event instanceof SnowEvent) {
            System.out.println("hello " + event.getWeather());
        }
    }
}

可以看到,SnowListener和RainListener类的onWeatherEvent方法会依据对应的天气Event进行过滤。

定义完了监听器以及事件之后,就还差广播器以及调用广播器播放事件的XXRunListener了。先定义一个事件广播器,包含了基础的添加监听器、移除监听器、播放事件的功能。

public interface EventMulticaster {
    void multicastEvent(WeatherEvent event);
    void addListener(WeatherListener weatherListener);
    void removeListener(WeatherListener weatherListener);
}

抽象广播器类

@Component
public abstract class AbstractEventMulticaster implements EventMulticaster{

	// 自动注入所有的天气监听器
    @Autowired
    private List<WeatherListener> listenerList;

	// 播放天气事件
    @Override
    public void multicastEvent(WeatherEvent event) {
        doStart();
        // 遍历监听器,然后播放天气事件
        listenerList.forEach(i -> i.onWeatherEvent(event));
        doEnd();
    }

    @Override
    public void addListener(WeatherListener weatherListener) {
        listenerList.add(weatherListener);
    }

    @Override
    public void removeListener(WeatherListener weatherListener) {
        listenerList.remove(weatherListener);
    }
	// 记录广播器开始调用
    abstract void doStart();
	// 记录广播器结束调用
    abstract void doEnd();
}

定义完了广播器,就运行广播器的XXRunListener了,下面定义一个WeatherRunListener,用于播放感兴趣的事件。

@Component
public class WeatherRunListener {

    @Autowired
    private WeatherEventMulticaster eventMulticaster;

    public void snow() {
        eventMulticaster.multicastEvent(new SnowEvent());
    }

    public void rain() {
        eventMulticaster.multicastEvent(new RainEvent());
    }

}

编写测试类测试
在这里插入图片描述

5. 总结

通过SpringBoot底层源码的分析以及模仿SpringBoot实现的自定义事件广播器,相信大家对广播器、监听器、事件以及调用广播器的XXRunListener运行监听类有了更深入的理解了。另外通过SpringBoot底层源码的分析后可以了解到,SpringBoot事件监听机制用的也是非常的广泛,除了文中介绍的EventPublishingRunListener监听器监听SpringApplication生命周期,还有ConfigFileApplicationListener会监听onApplicationEnvironmentPreparedEvent事件来加载配置文件application.properties的环境变量,所以说SpringBoot事件监听机制是非常重要的一个知识点,在SpringBoot的面试中也会经常面试到。

觉得作者写的不错的点个赞并关注作者。
本文代码在面的地址中已收录,文中出现的源码以及流程图都已收录进github中。
https://github.com/coderbruis/JavaSourceCodeLearning

Spring Boot 2.0 新特性系列 | 第二篇:新增事件ApplicationStartedEvent 推荐 Spring Boot/Cloud 视频: Java 微服务实践 - Spring Boot Java 微服务实践 - Spring Cloud 今天继续来聊Spring Boot 2.0的新特性。本文将具体说说2.0版本中的事件模型,尤其是新增的事件:ApplicationStartedEvent。 在Spring Boot 2.0中对事件模型做了一些增强,主要就是增加了Applica... 阅读详情

相关推荐

SpringBoot源码分析之bootstrap

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

2303_77877769的博客 942

spring 源码分析 事件广播 (SimpleApplicationEventMulticaster 观察者模式)

解析Springboot源码。博主采用版本是Springboot2.2.1,Spring5.2.0,新特性本系列后面也会着重分析。 整个系列是围绕springboot启动流程进行源码分析,在整个流程中,会遇到一些核心类或者核心流程,会着重讲解,所以篇幅可能会增多,做好准备。 源码分析 SimpleApplicationEventMulticaster 类结构分析: 实现了 Application...

Pazz 4793

低功耗手持设备电源设计:PCA9422与PIC18LF47K40组合实战

在电池供电的嵌入式系统中,电源管理直接影响续航与稳定性。可编程PMIC与低功耗MCU协同工作,可实现多路电源轨的动态电压调节与精细功耗控制。其原理在于,MCU负责策略调度,PMIC负责执行电压切换与硬件保护,通过I2C配置寄存器即可改变输出电压与工作模式,兼顾性能与能耗。该方案广泛应用于手持采集器、无线传感器节点、便携医疗设备等场景,尤其适合需要多电压轨、动态调节及长待机的小型产品。本文基于PCA9422 PMIC与PIC18LF47K40低功耗MCU的工程实践,详细讲解方案选型、电路互连、寄存器配置、低功

weixin_34343308的博客 300

十二、EventPublishingRunListener类

spring-boot-2.2.6.REALEASE的jar包的META-INF/spring.factories文件,有如下配置: # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener SpringApplicationRunListener是SpringApplicationRunL

pscool的博客 3274

React Vue Next.js Nuxt 底层原理与全栈能力对比

现代前端开发已超越单一框架语法学习,进入以组件化、响应式、虚拟DOM、SSR/SSG、客户端水合(Hydration)为核心的范式统一阶段。理解这些基础概念,是掌握React声明式UI哲学、Vue响应式系统(Proxy实现)、Next.js约定优于配置的服务端能力、Nuxt生态整合机制的前提。技术价值在于打破框架壁垒,实现跨栈协同与微前端集成;典型应用场景包括新闻聚合类SSR+SSE实时页面、企业级后台系统(Vue+Element Plus+C#)、AI Agent工作流编排等。本文聚焦四大框架共性内核与差

an4455的博客 379

Mid-360与FAST-LIO2实战:从点云采集到高精度建图全流程解析

本文详细解析了使用Livox Mid-360激光雷达与FAST-LIO2算法实现高精度实时建图的全流程。内容涵盖硬件准备、软件环境搭建、算法部署配置、数据采集实战,并重点探讨了多楼层场景下的参数调优与误差抑制策略,为机器人及无人机三维感知应用提供了实用指南。

prometheus9mon的博客 411

SimpleApplicationEventMulticaster学习

SimpleApplicationEventMulticaster

weixin_44877172的博客 1051

Bybit面经

这段总结描述了求职者在Bybit面试过程中的经历和相关问题,包括面试内容、求职者向面试官提问的问题以及关于Bybit的信息

郑泽洲的博客 2578

深入浅出Spring/SpringBoot 事件监听机制

监听器模型 说明 事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器); 在Java中,事件对象都是继承java.util.EventObject对象,事件监听器都是java.util.EventListener实例; EventObject对象不提供默认构造器,需要外部传递source参数,即用于记录并跟踪事件的来源; Spring事件 Spring事件对象为ApplicationEvent,继承EventObject,源码如下: public abst..

Javaesandyou的博客 1594

如何用zemax导入solidworks装配体

在zemax使用中,常常需要导入各种零件。如果光机结构较为复杂,那么手工一个一个加载零件,还需要在zmx文件中调整object的位置,非常麻烦。如果能将solidworks的装配体模型直接导入,则可以大幅减小工作量。具体过程为(非序列模式下): 1、新建或打开一个zemax空文件 2、在元件编辑器中,找到一个空文件,双击或直接展开三角符,在类型中找到zemax里的solidworks装配体,如图。...

wojiaoyyy的博客 1万+

Windows 11 IoT企业版LTSC 24H2/25H2中文版安装指南(含OEM激活教程)

本文详细介绍了Windows 11 IoT企业版LTSC 24H2/25H2中文版的安装与OEM激活全流程。从系统镜像准备、硬件兼容性检查到自动化安装配置,再到OEM激活方案(包括SLIC注入和数字许可证激活),为工业自动化、数字标牌等物联网设备提供稳定可靠的系统部署指南。

weixin_29198045的博客 8795

深入浅出springboot_深入浅出Spring/SpringBoot 事件监听机制

监听器模型说明事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器);在Java中,事件对象都是继承java.util.EventObject对象,事件监听器都是java.util.EventListener实例;EventObject对象不提供默认构造器,需要外部传递source参数,即用于记录并跟踪事件的来源;Spring事件Spring事件对象为Applicat...

weixin_39692830的博客 69

RT-Thread stm32h7使用串口DMA

stm32h7在rtthread中无法使用串口DMA,通过更改驱动代码使其支持串口DMA

qq_35141454的博客 2284

深入浅出springboot 源码_SpringBoot优雅退出 ?

背景我看了一些介绍SpringBoot源码的文章,里面都是从SpringApplication这个外部调用入口开始,重点介绍里面的listener监听器。今天我想想说说SpringBoot的优雅退出机制。调用入口调用SpringApplication开始启动SpringBoot@SpringBootApplicationpublic class Application { public stati...

weixin_39606911的博客 115

ESP32搭配FT6236触摸屏:从芯片手册到完整I2C驱动代码的保姆级解析

本文详细解析了ESP32搭配FT6236触摸屏的I2C驱动开发过程,从芯片手册解读到完整代码实现。通过硬件架构分析、寄存器配置、I2C通信封装及触摸数据处理等步骤,提供了一套完整的驱动解决方案,帮助开发者快速掌握ESP32与触摸屏的交互技术。

weixin_29174385的博客 291

YOLO+OpenCV强强联手:高精度跌倒检测技术实战解析

(0, 1), (0, 2), (1, 3), (2, 4), # 脖子和头部(3, 5), (5, 6), (5, 7), (7, 9), (5,11),# 左臂(4,6), (6, 12), (6,8) ,(8,10),# 右臂(11, 12), (11, 13), (13, 15), #左腿(12, 14), (14, 16) #右腿keypoints是检测到的人体关键点数据,frame是图像帧,boxes是检测到的边界框信息。定义了人体关键点之间的连接关系,用于绘制人体骨架。

yolo模型训练找我,AI游戏合作找我 2308

Spring Boot 2.x 启动全过程源码分析(全)

Spring Boot 2.x 启动全过程源码分析(全) 原创: 不羁码农 Java技术栈 Java技术栈 微信号 javastack 功能介绍 专注分享Java技术干货,包括多线程、JVM、Spring Boot、Spring Cloud、Intellij IDEA、Dubbo、Zookeeper、Redis、架构设计、微服务、消息队列、Git、面试题、程序员攻略、最新动态等。 201...

java_2017_csdn的博客 393

【ComfyUI】Wan2.2 Animate 背景保留动作迁移视频生成

本文介绍了一个基于ComfyUI的高精度视频生成工作流"Wan2.2 Animate",该系统融合动作捕捉、图像拼接和LoRA加权等技术,能自动识别人脸并完成精准动作迁移,同时保持背景稳定。工作流采用模块化设计,包含输入提取、图像融合、模型加载等关键节点,核心模型Wan2.2-Animate-14B支持fp8精度与BlockSwap控制。流程分为图像输入、动作提取、多图合成等五个阶段,通过文本提示编码器和图像语义编码器实现语义驱动的动作生成,最终输出高质量视频,适用于虚拟形象驱动和

Mr数据杨 2254

深度解析 Spring Boot 启动运行机制

在Java后端开发生态中,Spring Boot凭借“约定优于配置”的核心理念,极大简化了传统Spring应用的配置与部署流程。

spb229443329的专栏 684

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java面试-Spring Boot 启动流程源码分析

《Java面试200问》系列之Spring Boot启动流程源码分析,深入解析从main方法到完整应用的全过程。文章通过拆解SpringApplication.run()方法,揭示其两阶段启动机制:首先初始化环境(推断Web类型、加载初始化器和监听器),然后执行核心启动流程。作者结合源码详细讲解了自动配置、事件监听等关键机制,帮助读者理解Spring Boot"约定优于配置"的设计哲学。该分析既可作为面试备战资料,也能加深开发者对框架底层原理的认知。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Spring神器:时间多播器ApplicationEventMulticaster

Spring的时间多播器(ApplicationEventMulticaster)是Spring事件机制的核心组件之一。它负责将Spring应用程序中发布的事件分发给感兴趣的监听器。在Spring中,事件机制是一种发布-订阅模式的实现。事件发布者将事件发布到ApplicationEventMulticaster中,然后ApplicationEventMulticaster将事件分发给所有注册的事件监听器。这种机制使得Spring应用程序中的组件可以更好地解耦,并且可以更灵活地响应事件。

five-five 1738

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Spring 5 SimpleApplicationEventMulticaster 源码分析

SimpleApplicationEventMulticaster SimpleApplicationEventMulticaster 是 AbstractApplicationContext 的默认事件多播器 /** * Simple implementation of the {@link ApplicationEventMulticaster} interface. * <p>简单实现的 ApplicationEventMulticaster 接口</p> * <p

qq_30321211的博客 1551

Spring中事件驱动开发,ApplicationListener及ApplicationEventMulticaster

Spring中事件驱动开发:ApplicationListener及ApplicationEventMulticaster的使用

2201_75955594的博客 1657

SpringBoot源码解析(三)ApplicationStartingEvent

上篇文章分析了SpringBoot事件发布机制的大致原理,并且提到了SpringBoot启动过程中的第一个事件ApplicationStartingEvent,以及监听了该事件的四个监听器: DelegatingApplicationListener LoggingApplicationListener LiquibaseServiceLocatorApplicationListener BackgroundPreinitializer 首先还是先简单说下为什么这四个监听器能监听到Application

m0_37298252的博客 1828
上一篇: 实战电商后端系统(一)—— 项目搭建以及Dubbo+SpringBoot+Oauth2+Swagger2的集成
下一篇: 二进制运算以及源码、补码、反码概念讲解
CoderBruis
博客等级 码龄10年 4444粉丝 102原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值