Spring Boot源码分析五:启动流程

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

resolveDefaultEventType

传入的参数类型new ApplicationStartingEvent(bootstrapContext, this.application, this.args)

继承结构及其构造方法。

java 复制代码
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
   return ResolvableType.forInstance(event);
}

实例方法resolveDefaultEventType调用了本身的静态方法forInstance,传入的参数就是透传事件类型实例对象。

java 复制代码
public static ResolvableType forInstance(Object instance) {
   Assert.notNull(instance, "Instance must not be null");
   // 判断是不是这个类型 根据上面的时间类型的结构发现不是
   if (instance instanceof ResolvableTypeProvider) {
      ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
      if (type != null) {
         return type;
      }
   }
   // 传入了事件实例对象的类型Class
   return ResolvableType.forClass(instance.getClass());
}

接着调用静态方法forClass。参数换成了事件对象的类型Class实例。

操作很简单直接new了一个ResolvableType对象。

java 复制代码
public static ResolvableType forClass(@Nullable Class<?> clazz) {
   return new ResolvableType(clazz);
}

看看这个构造函数做了什么。

java 复制代码
private ResolvableType(@Nullable Class<?> clazz) {
// clazz不为空,将事件类型的Class对象赋值给成员变量resolved
   this.resolved = (clazz != null ? clazz : Object.class);
   // 下面有Class的继承关系图一 Class 实现了Type接口
   // 将事件类型的Class对象赋值给成员变量type
   this.type = this.resolved;
   this.typeProvider = null;
   this.variableResolver = null;
   this.componentType = null;
   this.hash = null;
}
  • 图一

只给resolved和type进行了赋值。

getApplicationListeners

java 复制代码
protected Collection<ApplicationListener<?>> getApplicationListeners(
      ApplicationEvent event, ResolvableType eventType) {
// 根据事件构造函数的跟进发现这个source是SpringApplication的实例对象
   Object source = event.getSource();
   // 这里就是SpringApplication的Class对象
   Class<?> sourceType = (source != null ? source.getClass() : null);
   // 组装一个key
   ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

   // Potential new retriever to populate
   CachedListenerRetriever newRetriever = null;

   // Quick check for existing entry on ConcurrentHashMap
   // 想从缓存中获取Listener
   CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
   // 缓存不存在
   if (existingRetriever == null) {
      // Caching a new ListenerRetriever if possible
      if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                  (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
         newRetriever = new CachedListenerRetriever();
         existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
         if (existingRetriever != null) {
            newRetriever = null;  // no need to populate it in retrieveApplicationListeners
         }
      }
   }

   if (existingRetriever != null) {
      Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
      if (result != null) {
         return result;
      }
      // If result is null, the existing retriever is not fully populated yet by another thread.
      // Proceed like caching wasn't possible for this current local attempt.
   }

// 这个方法里面感觉有点类似于业务代码 就不看了  弯弯绕绕的
   return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

multicastEvent

java 复制代码
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   // 遍历支持这个事件的listener
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
      // 调用listener
         invokeListener(listener, event);
      }
   }
}

invokeListener

java 复制代码
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 这是一个null
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         doInvokeListener(listener, event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
   // 调用listener
      doInvokeListener(listener, event);
   }
}

doInvokeListener

调用的是listener的onAppicationEvent方法,传入了事件对象。这个事件对象中的source是SpringApplication的对象实例。可以根据不同阶段知道SpringApplication中能获取到什么来处理自己的业务。

java 复制代码
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
   try {
   // 调用listener的onApplicationEvent方法
      listener.onApplicationEvent(event);
   }
   catch (ClassCastException ex) {
      String msg = ex.getMessage();
      if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
            (event instanceof PayloadApplicationEvent &&
                  matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
         // Possibly a lambda-defined listener which we could not resolve the generic event type for
         // -> let's suppress the exception.
         Log loggerToUse = this.lazyLogger;
         if (loggerToUse == null) {
            loggerToUse = LogFactory.getLog(getClass());
            this.lazyLogger = loggerToUse;
         }
         if (loggerToUse.isTraceEnabled()) {
            loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
         }
      }
      else {
         throw ex;
      }
   }
}

至此我们已经看完了listener的是如何加载并初始化的。也了解了Spring的所谓listener的整个工作流程。

简单总结一下:

初始化listener

  • 就是初始化了一个SpringApplicationRunListeners实例。这个实例中包含了一个EventPublishingRunListener实例。
  • EventPublishingRunListener实例中有SpringApplication的实例、arg参数、SimpleApplicationEventMulticaster对象。
  • SimpleApplicationEventMulticaster中有所有的ApplicationListener

至此我们已经拥有了所有的listener。

再看如何发送消息的流程

  • 调用SpringApplicationRunListeners的方法,比如starting。它的方法里面再调用自己的doWithListeners方法。
  • 遍历调用SpringApplicationRunListener,这里就只有EventPublishingRunListener。
  • 执行对应SpringApplicationRunListener的方法。都以starting方法为例。
  • 调用SimpleApplicationEventMulticaster的方法multicastEvent。并且构造了事件作为参数传递。
  • 在调用SimpleApplicationEventMulticaster的方法multicastEvent的重构方法。参数是事件和对事件的解析。
  • ulticastEvent这个方法里面获取支持处理对应时间的listener,然后遍历这些listener,调用listener的onApplicationEvent方法。

OK 今天先到这里吧。

See you next time :)

相关推荐
愤怒的代码12 分钟前
Spring Boot对访问密钥加解密——HMAC-SHA256
java·spring boot·后端
带多刺的玫瑰13 分钟前
Leecode刷题C语言之切蛋糕的最小总开销①
java·数据结构·算法
栗豆包28 分钟前
w118共享汽车管理系统
java·spring boot·后端·spring·tomcat·maven
夜半被帅醒35 分钟前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql
万亿少女的梦16841 分钟前
基于Spring Boot的网络购物商城的设计与实现
java·spring boot·后端
醒了就刷牙1 小时前
黑马Java面试教程_P9_MySQL
java·mysql·面试
m0_748233641 小时前
SQL数组常用函数记录(Map篇)
java·数据库·sql
编程爱好者熊浪2 小时前
JAVA HTTP压缩数据
java
吴冰_hogan2 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
开心工作室_kaic2 小时前
springboot485基于springboot的宠物健康顾问系统(论文+源码)_kaic
spring boot·后端·宠物