srpingboot web - 启动 监听器

Posted Sniper_ZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了srpingboot web - 启动 监听器相关的知识,希望对你有一定的参考价值。

接上一篇

一. getRunListeners()

在run() 方法中调用了 getRunListeners(args) 方法, 先看一下这个方法干了什么

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

加载配置 spring.factories 中的配置, 

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

然后创建配置中的 EventPublishingRunListener , 封装到  SpringApplicationRunListeners 类中的 this.listeners 属性中.

1. 创建 EventPublishingRunListener

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

1. 这里创建了一个多播器: SimpleApplicationEventMulticaster

  

 2. 将容器中10个监听器放入多播器中.

上一篇提到过, 容器中加载了10个监听器, 放在 this.listeners 属性中.

  这里的顺序与配置的读取顺序不同, 是经过排序过的.

addApplicationListener()
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}
ListenerRetriever 是 AbstractApplicationEventMulticaster 的一个内部类. 所以监听器是放在 一个内部类的 applicationListeners 属性中:
public final Set<ApplicationListener<?>> applicationListeners;

 

二. listeners.starting()

public void starting() {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}

此处的 this.listeners 中的 this -> SpringApplicationRunListeners .

所以调用的是 EventPublishingRunListener的starting() 方法.

//EventPublishingRunListener.java
@Override
public void starting() {
   //创建Application的启动事件, 并进行多波
this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); }

看一下 multicastEvent 方法:

//SimpleApplicationEventMulticaster.java
@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));
  // getApplicationListeners 会对监听器进行过滤
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
     //获取线程池, 暂时为null Executor executor
= getTaskExecutor(); if (executor != null) {
       //异步发送事件 executor.execute(()
-> invokeListener(listener, event)); } else {
       //同步发送事件 invokeListener(listener, event); } } }

 

getApplicationListeners()
protected boolean supportsEvent(
        ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

    GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
            (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
    return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

1.  判断监听器是否为 GenericApplicationListener 类型

  |-> 如果是, 则调用其 supportsEventType() 和 supportsSourceType() , 且同时满足, 才可以. 否则会被过滤掉

  |-> 如果不是, 则 使用GenericApplicationListenerAdapter 进行适配转换, 然后 调用上面两个方法, 同时满足, 才可以. 否则会被过滤掉

满足条件的有4个监听器:

 

 此处看一下 LoggingApplicationListener 的两个方法执行:

1. supportsEventType()

private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
            ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class,
            ContextClosedEvent.class, ApplicationFailedEvent.class };

@Override
public boolean supportsEventType(ResolvableType resolvableType) {
    return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
}

@Override
public boolean supportsSourceType(Class<?> sourceType) {
    return isAssignableFrom(sourceType, SOURCE_TYPES);
}

private boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
    if (type != null) {
        for (Class<?> supportedType : supportedTypes) {
            if (supportedType.isAssignableFrom(type)) {
                return true;
            }
        }
    }
    return false;
}

可以看到, 这里他支持5中类型, 其中正好就有当前发布的 ApplicationStartingEvent 事件.

2. supportsSourceType()

private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class,
            ApplicationContext.class };
@Override
public boolean supportsSourceType(Class<?> sourceType) {
  //这里调用的还是上面的那个方法, 只是传入参数不同
return isAssignableFrom(sourceType, SOURCE_TYPES); }

 

invokeListener() 
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  //不管 errorhandler 是否为null, 都会调用 doInvokeListener 方法 ErrorHandler errorHandler
= getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let\'s suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isDebugEnabled()) { logger.debug("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }

这里就是调用 监听器的  onApplicationEvent 方法, 并传入要多波的事件.

这里仍然来看 LoggingApplicationListener 的 onApplicationEvent 方法:

@Override
public void onApplicationEvent(ApplicationEvent event) {
  //Application启动
if (event instanceof ApplicationStartingEvent) { onApplicationStartingEvent((ApplicationStartingEvent) event); }
  //环境准备完成
else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); }
  //Application 准备完
else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); }
  //容器关闭
else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); }
  //Application启动失败
else if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent(); } }

不同的事件进来, 执行不同的方法. 

以上是关于srpingboot web - 启动 监听器的主要内容,如果未能解决你的问题,请参考以下文章

java Web 启动时自动执行代码的几种方式

基于srpingboot使用poi导出人事月度报表

基于srpingboot使用poi导出人事月度报表

基于srpingboot使用poi导出人事月度报表

java web项目启动时自动加载自定义properties文件

Spring Boot 学习笔记