从servlet到springboot(2) springboot启动 run方法解析 之事件监听 listeners.starting()为止

上一篇中我们主要看了SpringApplication的初始化,接下去几篇我们着重看下run方法

<---------

public ConfigurableApplicationContext run(String... args) {
    // 计时工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        // 创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        Banner printedBanner = printBanner(environment);
        // 创建SpringBoot上下文
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
----->

以上是run方法的全部,大致归纳下就是:

1.初始化StopWatch,调用其start方法开始计时.

2.调用configureHeadlessProperty设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能

3.调用SpringApplicationRunListeners#starting

4.创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数.调用prepareEnvironment方法.

5.打印banner

6.创建SpringBoot上下文

7.初始化FailureAnalyzers

8.调用prepareContext

9.调用AbstractApplicationContext#refresh方法,并注册钩子

10.在容器完成刷新后,依次调用注册的Runners

11.调用SpringApplicationRunListeners#finished

12.停止计时

---->

1.2跳过

3.  SpringApplicationRunListeners listeners = getRunListeners(args);这行代码我们深入分析

---->

Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
重点在第三句代码,我们发现这行代码和第一篇中的inilizer方法很类似,只是现在传入的clazz是SpringApplicationRunListener,在jar包中的META-INF/spring-factories文件中以SpringApplicationRunListener为key的value只有一个

是EventPublishingRunListner。我们深入

SpringApplicationRunListeners类发现
class SpringApplicationRunListeners {
    private final Log log;
    private final List<SpringApplicationRunListener> listeners;只有两个属性,一个是日志相关,还有一个是SpringApplicationRunListener的集合,而实际上listeners只有一个元素

在springboot中 SpringApplicationRunListener主要支持四种事件的监听,我们通过源码来看

SpringApplicationRunListener接口定义的方法有

public interface SpringApplicationRunListener {
    void started();

    void environmentPrepared(ConfigurableEnvironment var1);

    void contextPrepared(ConfigurableApplicationContext var1);

    void contextLoaded(ConfigurableApplicationContext var1);

    void finished(ConfigurableApplicationContext var1, Throwable var2);
}

springboot中继承SpringApplicationRunListener的实现类只有EventPublishingRunListener

EventPublishingRunListener中

public void started() {
    this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

这里可以看到这里发布了一个事件ApplicationStartedEvent,而这个started方法是在run方法中的这个位置实现的

  SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        // 创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);

从名字也能看出这是springboot容器启动事件

然后我们发现在

ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

这句代码中进入之后

---->

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
  。。。。。
    listeners.environmentPrepared(environment);//这句话跟踪到

。。。。。。

EventPublishingRunListener的environmentPrepared方法可以看到ApplicationEnvironmentPreparedEvent
}

---->

我们还可以在
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);以及
listeners.finished(context, (Throwable)null);两句代码中发现还有两个事件

ApplicationPreparedEvent

ApplicationFailedEvent

总结一下就是springboot在容器初始化过程中会初始化

SpringApplicationRunListeners,其中的SpringApplicationRunListener集合只包含了一个EventPublishingRunListener

这个listener会分发四种事件,分别是

ApplicationStartedEvent

ApplicationEnvironmentPreparedEvent

ApplicationPreparedEvent

ApplicationFailedEvent

这里我们按照分析run方法的顺序,只分析ApplicationStartedEvent事件

---------------------------------------------------->
    listeners.starting();
  --------->

public void started() {
    Iterator var1 = this.listeners.iterator();

    while(var1.hasNext()) {
        SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
        listener.started();
    }

}这里会依次遍历SpringApplicationRunListeners中的SpringApplicationRunListener集合,前面我们已经说过,这个集合中只有一个元素,所以就是执行EventPublishingRunListner中的started方法,我们跟入

----->

this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));

initialMulticaster是一个时间广播器,具体我们在后续的refresh方法中详解,现在就理解成发布事件的东西

跟踪进入

-------->
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
  。。。。。。。
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
     。。。。。。

首先我们分析 getApplicationListeners这句话

------->

protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event) {
        Object source = event.getSource();
        Class<?> sourceType = (source != null ? source.getClass() : null);
        ListenerCacheKey cacheKey = new ListenerCacheKey(event.getClass(), sourceType);

        // Quick check for existing entry on ConcurrentHashMap...
        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)))) {
            // Fully synchronized building and caching of a ListenerRetriever
            synchronized (this.retrievalMutex) {
                retriever = this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                }
                retriever = new ListenerRetriever(true);
                Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(event, sourceType, retriever);
                this.retrieverCache.put(cacheKey, retriever);
                return listeners;
            }
        }
        else {
            // No ListenerRetriever caching -> no synchronization necessary
            return retrieveApplicationListeners(event, sourceType, null);
        }
    }
 

代码比较长,简要的概括下就是

该方法首先构建ListenerCacheKey,然后查询缓存中是否有的话,直接返回.否则调用retrieveApplicationListeners方法.如果

this.beanClassLoader == null || 
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && 
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) 为真的话,则调用retrieveApplicationListeners方法时会进行加锁.并将方法的返回值加入缓存.一般都会放入缓存的.

这里要跟进一下这块代码

retrieveApplicationListeners方法,代码如下:

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
        ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {

    LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.retrievalMutex) {
        listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
    }
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                retriever.applicationListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }
    if (!listenerBeans.isEmpty()) {
        BeanFactory beanFactory = getBeanFactory();
        for (String listenerBeanName : listenerBeans) {
            try {
                Class<?> listenerType = beanFactory.getType(listenerBeanName);
                if (listenerType == null || supportsEvent(listenerType, eventType)) {
                    ApplicationListener<?> listener =
                            beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            retriever.applicationListenerBeans.add(listenerBeanName);
                        }
                        allListeners.add(listener);
                    }
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Singleton listener instance (without backing bean definition) disappeared -
                // probably in the middle of the destruction phase
            }
        }
    }
    AnnotationAwareOrderComparator.sort(allListeners);
    return allListeners;
}
简要概括下

处理逻辑如下:

初始化allListeners, listeners,listenerBeans,对于当前场景来说. listeners 中的元素如下:

org.springframework.boot.context.config.ConfigFileApplicationListener, 
org.springframework.boot.context.config.AnsiOutputApplicationListener, 
org.springframework.boot.logging.LoggingApplicationListener, 
org.springframework.boot.logging.ClasspathLoggingApplicationListener, 
org.springframework.boot.autoconfigure.BackgroundPreinitializer, 
org.springframework.boot.context.config.DelegatingApplicationListener, 
org.springframework.boot.builder.ParentContextCloserApplicationListener, 
org.springframework.boot.ClearCachesApplicationListener, 
org.springframework.boot.context.FileEncodingApplicationListener, 
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

listenerBeans中的元素为空.

遍历listeners, listenerBeans 如果监听器是否支持指定的事件则加入到allListeners.

排序
对于当前场景ApplicationStartedEvent支持的listeners如下:

----最后一个listener是我自己定义的

然后我们回到

multicastEvent方法
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(new Runnable() {
            @Override
            public void run() {
               invokeListener(listener, event);
            }
         });
      }
      else {
         invokeListener(listener, event);
      }
   }
}

接下去就是遍历之前返回的几个linstener,这里根据有没有传入excutor来判断要不要进行异步操作,

我们进入 invokeListener(listener, event)方法

protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
  。。。。。。
   if (errorHandler != null) {
      try {
         listener.onApplicationEvent(event);
      }
    。。。。。。。。

这里面就是直接调用了listener的onApplicationEvent(event)方法所以我们只要分析这几个listener的onApplicationEvent(event)方法即可

通过进入四个springboot自带的listners候发现,除了LoggingApplicationListener内部做了一些日志相关的动作之外,其他的什么也没做,当然我们也可以自己扩展,比如第五个就是我自己扩展的

下面贴一下第五个监听器的代码

public class MyApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        SpringApplication app = event.getSpringApplication();
        System.out.println("MyApplicationStartedEventListener start");
    }
}

当代码debug到run方法的listeners.starting时候,MyApplicationStartedEventListener start就会被打印出来。

这一篇就暂时分析到这里。

猜你喜欢

转载自blog.csdn.net/m0_37139189/article/details/86220023
今日推荐