https://www.bilibili.com/video/BV12C4y1s7dR?p=11

监听器模式要素

  • 事件
  • 监听器
  • 广播器
  • 触发机制

Springboot中监听模式总结

  1. 在SpringApplication初始化中从META_INF/spring.factories获取Listeners
  2. 创建监听器
  3. 创建广播器
  4. 将监听器在广播器中进行注册
  5. 事件触发时,监听器监听到,同时将创建的事件传给广播器,执行广播器,通过回调调用实际事件中方法

Springboot中监听器各要素

事件
  1. public abstract class ApplicationEvent extends EventObject {
  2. /** use serialVersionUID from Spring 1.2 for interoperability. */
  3. private static final long serialVersionUID = 7099057708183571937L;
  4. /** System time when the event happened. */
  5. private final long timestamp;
  6. /**
  7. * Create a new {@code ApplicationEvent}.
  8. * @param source the object on which the event initially occurred or with
  9. * which the event is associated (never {@code null})
  10. */
  11. public ApplicationEvent(Object source) {
  12. super(source);
  13. this.timestamp = System.currentTimeMillis();
  14. }
  15. /**
  16. * Return the system time in milliseconds when the event occurred.
  17. */
  18. public final long getTimestamp() {
  19. return this.timestamp;
  20. }
  21. }
监听器
  1. @FunctionalInterface
  2. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  3. /**
  4. * Handle an application event.
  5. * @param event the event to respond to
  6. */
  7. void onApplicationEvent(E event);
  8. }
广播器
  1. public interface ApplicationEventMulticaster {
  2. void addApplicationListener(ApplicationListener<?> listener);
  3. void addApplicationListenerBean(String listenerBeanName);
  4. void removeApplicationListener(ApplicationListener<?> listener);
  5. void removeApplicationListenerBean(String listenerBeanName);
  6. void removeAllListeners();
  7. void multicastEvent(ApplicationEvent event);
  8. void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
  9. }
事件触发,如:

SpringAppliation中listeners.starting();

源码梳理

在SpringApplication初始化中setListeners

在SpringApplication构造函数中,执行setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2. this.resourceLoader = resourceLoader;
  3. Assert.notNull(primarySources, "PrimarySources must not be null");
  4. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  5. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  6. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  7. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  8. this.mainApplicationClass = deduceMainApplicationClass();
  9. }

执行SpringFactoriesLoader类中loadSpringFactories方法,将jar包中所有META_INF/spring.factories文件中的属性进行加载(在setInitializers中已经加载,所以在setListeners直接从MultiValueMap缓存中取)

  1. Enumeration<URL> urls = (classLoader != null ?
  2. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  3. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

调用链:

SpringApplication#init() --> SpringApplication#getSpringFactoriesInstances() --> SpringFactoriesLoader#loadFactoryNames() --> SpringFactoriesLoader#loadSpringFactories()

loadSpringFactories代码:

  1. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  2. MultiValueMap<String, String> result = cache.get(classLoader);
  3. if (result != null) {
  4. return result;
  5. }
  6. try {
  7. Enumeration<URL> urls = (classLoader != null ?
  8. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  9. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  10. result = new LinkedMultiValueMap<>();
  11. while (urls.hasMoreElements()) {
  12. URL url = urls.nextElement();
  13. UrlResource resource = new UrlResource(url);
  14. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  15. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  16. String factoryTypeName = ((String) entry.getKey()).trim();
  17. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  18. result.add(factoryTypeName, factoryImplementationName.trim());
  19. }
  20. }
  21. }
  22. cache.put(classLoader, result);
  23. return result;
  24. }
  25. catch (IOException ex) {
  26. throw new IllegalArgumentException("Unable to load factories from location [" +
  27. FACTORIES_RESOURCE_LOCATION + "]", ex);
  28. }
  29. }

调用createSpringFactoriesInstances方法进行实例化

  1. private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
  2. ClassLoader classLoader, Object[] args, Set<String> names) {
  3. List<T> instances = new ArrayList<>(names.size());
  4. for (String name : names) {
  5. try {
  6. Class<?> instanceClass = ClassUtils.forName(name, classLoader);
  7. Assert.isAssignable(type, instanceClass);
  8. Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
  9. T instance = (T) BeanUtils.instantiateClass(constructor, args);
  10. instances.add(instance);
  11. }
  12. catch (Throwable ex) {
  13. throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
  14. }
  15. }
  16. return instances;
  17. }

将获取到的listeners注册到List<ApplicationListener<?>> listeners中

  1. public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
  2. this.listeners = new ArrayList<>(listeners);
  3. }
获取SpringApplicationRunListener
  1. SpringApplicationRunListeners listeners = getRunListeners(args);
  1. private SpringApplicationRunListeners getRunListeners(String[] args) {
  2. Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  3. return new SpringApplicationRunListeners(logger,
  4. getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
  5. }

key为org.springframework.boot.SpringApplicationRunListener,从之前缓存的map中取出SpringApplicationRunListener,value为EventPublishingRunListener

对EventPublishingRunListener进行初始化,创建SimpleApplicationEventMulticaster广播器

  1. public EventPublishingRunListener(SpringApplication application, String[] args) {
  2. this.application = application;
  3. this.args = args;
  4. this.initialMulticaster = new SimpleApplicationEventMulticaster();
  5. for (ApplicationListener<?> listener : application.getListeners()) {
  6. this.initialMulticaster.addApplicationListener(listener);
  7. }
  8. }

将监听器注册到广播器中:

  1. for (ApplicationListener<?> listener : application.getListeners()) {
  2. this.initialMulticaster.addApplicationListener(listener);
  3. }
事件触发

SpringApplication中listeners.starting();

执行到EventPublishingRunListener中

  1. @Override
  2. public void starting() {
  3. this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
  4. }

进行广播事件:

  1. @Override
  2. public void multicastEvent(ApplicationEvent event) {
  3. multicastEvent(event, resolveDefaultEventType(event));
  4. }
  5. @Override
  6. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  7. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  8. Executor executor = getTaskExecutor();
  9. for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  10. if (executor != null) {
  11. executor.execute(() -> invokeListener(listener, event));
  12. }
  13. else {
  14. invokeListener(listener, event);
  15. }
  16. }
  17. }

然后去调用具体事件的onApplicationEvent方法

Spring源码之Springboot中监听器介绍的更多相关文章

  1. Spring源码之AbstractApplicationContext中refresh方法注释

    https://blog.csdn.net/three_stand/article/details/80680004 refresh() prepareRefresh(beanFactory) 容器状 ...

  2. Spring源码系列(一)--详解介绍bean组件

    简介 spring-bean 组件是 IoC 的核心,我们可以通过BeanFactory来获取所需的对象,对象的实例化.属性装配和初始化都可以交给 spring 来管理. 针对 spring-bean ...

  3. Mybatis源码解读-SpringBoot中配置加载和Mapper的生成

    本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...

  4. Spring源码解析-Advice中的Adapter模式

    在spring中与通知相关的类有: 以Advice结尾的通知接口    MethodBeforeAdvice    AfterReturningAdvice   ThrowsAdvice 以Inter ...

  5. spring源码学习之路---AOP初探(六)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 最近工作很忙,但当初打算学习 ...

  6. spring源码学习之bean的加载(一)

    对XML文件的解析基本上已经大致的走了一遍,虽然没有能吸收多少,但是脑子中总是有些印象的,接下来看下spring中的bean的加载,这个比xml解析复杂的多.这个加载,在我们使用的时候基本上是:Bea ...

  7. spring源码学习(一)--AOP初探

    LZ以前一直觉得,学习spring源码,起码要把人家的代码整体上通读一遍,现在想想这是很愚蠢的,spring作为一个应用平台,不是那么好研究透彻的,而且也不太可能有人把spring的源码全部清楚的过上 ...

  8. Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?

    阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ...

  9. Spring源码分析(十四)从bean的实例中获取对象

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在getBean方法中,getObjectForBeanlnstance ...

随机推荐

  1. 微信小程序 - 重置checkbox样式

    /* 未选中的 背景样式 */ checkbox .wx-checkbox-input { border-radius: 50%;/* 圆角 */ width: 30rpx; /* 背景的宽 */ h ...

  2. SQL Server Management Studio (SSMS)单独安装,仅安装连接工具

    简单来说,SSMS是用于远程连接数据库与执行管理任务的一个工具.当安装SQL SERVER时,会默认安装.但也可以单独安装在不是数据库服务器的主机上. SQL Server Management St ...

  3. 转 RabbitMQ 入门教程(PHP版) 使用rabbitmq-delayed-message-exchange插件实现延迟功能

    延迟任务应用场景 场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单. 场景三:过1分钟给新 ...

  4. spring boot:spring security整合jwt实现登录和权限验证(spring boot 2.3.3)

    一,为什么使用jwt? 1,什么是jwt? Json Web Token, 它是JSON风格的轻量级的授权和身份认证规范, 可以实现无状态.分布式的Web应用授权 2,jwt的官网: https:// ...

  5. go mod模式,引入自己的包,goland飘红

  6. spring boot:用redis+lua实现表单接口的幂等性(spring boot 2.2.0)

    一,什么是幂等性? 1,幂等: 幂等操作:不管执行多少次,所产生的影响都和一次执行的影响相同. 幂等函数或幂等方法:可以使用相同的参数重复执行,并能获得相同的结果的函数/方法. 这些函数/方法不用担心 ...

  7. Spark如何进行动态资源分配

    一.操作场景 对于Spark应用来说,资源是影响Spark应用执行效率的一个重要因素.当一个长期运行的服务,若分配给它多个Executor,可是却没有任何任务分配给它,而此时有其他的应用却资源紧张,这 ...

  8. SQL DELETE语句如何让表使用别名的方法

    DELETE 别名 FROM 表名称 别名 WHERE 列名称 = 值

  9. 2020主流国产BI产品对比

    国产BI软件由于具备较强的本土特性,可以很好地适应国内用户的使用习惯,越来越多被国内用户使用.目前国内BI产品很多,可谓百家争鸣,如何从众多的BI产品中选择适合自己的呢?这里我们对比一下目前国内主流的 ...

  10. 李志杰的C语言程序设计第一次作业

    这个作业属于C语言程序设计课程 : https://edu.cnblogs.com/campus/zswxy/CST2020-2 这个作业要求在哪里: https://edu.cnblogs.com/ ...