Spring

[Spring]详解Spring中的事件监听器模式

2021-01-16  本文已影响0人  AbstractCulture

1. 事件监听器模式的重要因素

关于事件监听器模式,如果你不够熟悉,可以在我的上篇博客得到解答->点我前往

2. ApplicationEvent

2.1 接口清单

值得一提的是,Spring中的ApplicationEvent是内置事件源的,这意味着在监听器中可以获取事件源,而Spring中的事件源通常为容器本身.
同时,事件发生的同时,会记录当前系统时间戳

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import java.util.EventObject;

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability. */
    private static final long serialVersionUID = 7099057708183571937L;

    /** System time when the event happened. */
    private final long timestamp;


    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})<br>
     * 创建一个应用事件,其中source为事件源,并且不能为空.
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }


    /**
     * Return the system time in milliseconds when the event happened.<br>
     * 返回当前事件发生时的系统时间
     */
    public final long getTimestamp() {
        return this.timestamp;
    }

}

2.2 UML

UML

看类图可以知道,ApplicationEvent继承自JDK的EventObject,同时还有一个抽象子类ApplicationContextEvent,为什么要再抽象出这个子类呢,因为Object本身的含义太过广泛,Spring为了定义容器事件,强制约束source对象本身的类型为ApplicationContext.
最后从这个抽象类,衍生了一系列单一职责的事件。分别对应容器的关闭、刷新、启动、停止等阶段.

2.3 PayloadApplicationEvent

Spring 4.2 后推出一个一个基于泛型对事件进行包装的类,在此之前,发布的Event都必须继承自ApplicationEvent.加入Payload机制后,在发布事件的时候,可以传输任意的Object,Spring内部都会用PayloadApplicationEvent对事件进行包装.

具体的改进在org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)可以做更加深入的了解。

default

3. ApplicationListener

3.1 接口清单

函数式接口,同时监听的事件需为继承自ApplicationEvent的类,如果Spring为4.2后的,可以不受这个限制,因为内部使用PayloadApplicationEvent进行了包装,在事件源发布事件时,会触发onApplicationEvent通知监听器。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     * 处理应用事件
     */
    void onApplicationEvent(E event);

}

3.2 UML

UML

3.3 注解支持-@EventListener

Spring4.2后,对事件监听器增加了注解的支持,无需实现接口,只需要通过@EventListener来直接在需要响应的方法上标注即可,配合Async@Order注解还可以支持异步与消费顺序声明.

注意,注解标注的方法上,最好声明为void,如果返回类型为数组或者集合,Spring会将每个元素作为新的事件进行发布。

4.ApplicationEventPublisher&ApplicationEventMulticaster

4.1 ApplicationEventPublisher

4.1.1 接口清单

通常在SpringIOC中,容器本身会作为ApplicationEventPublisher去进行事件的发布.
同时,开发者还可以通过Aware接口访问到ApplicationEventPublisher实例.来发布自定义事件.

@FunctionalInterface
public interface ApplicationEventPublisher {
    // 通知所有与此应用程序注册的匹配侦听器一个应用程序事件。
    // 事件可以是框架事件(例如ContextRefreshedEvent)或特定于应用程序的事件。
    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }

    // 通知所有与此应用程序注册的匹配侦听器事件。
    // 如果指定的事件不是ApplicationEvent,则将其包装在PayloadApplicationEvent中。
    void publishEvent(Object event);

}
4.1.2 UML
UML

这里再次验证,容器即为IOC的ApplicationEventPublisher.

4.2 ApplicationEventMulticaster

Spring中的publisher只提供了发布事件的接口,然而一个事件监听器模式少不了注册监听器这件事情,ApplicationEventMulticaster就是为了解决这件事而产生的。

4.2.1 接口清单
public interface ApplicationEventMulticaster {

    void addApplicationListener(ApplicationListener<?> listener);

    void addApplicationListenerBean(String listenerBeanName);

    void removeApplicationListener(ApplicationListener<?> listener);

    void removeApplicationListenerBean(String listenerBeanName);

    void removeAllListeners();

    void multicastEvent(ApplicationEvent event);

    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}

从接口清单我们可以看到,ApplicationEventMulticaster支持添加监听器、移除监听器、发布事件。

4.2.2 UML
UML
    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);
            }
        }
    }

4.3 为什么要同时定义ApplicationEventMulticaster和ApplicationEventPublisher?

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)中,我们可以看到,容器内部是怎么发送事件的.

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");

        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        }
        else {
            // 如果事件对象不是ApplicationEvent,则使用PayloadApplicationEvent进行包装
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
            }
        }

        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            // 重点,可以看到publishEvent其实是通过ApplicationEventMulticaster进行真正的事件发布
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

        // Publish event via parent context as well...
        // 一些递归的操作
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }

简单地说,就是ApplicationEventPublisher是一个简单的事件发布接口,只负责声明入口,而ApplicationEventMulticaster负责处理真正的事件发布逻辑。这其实是一种委托的思想.你可以在Spring随处发现这种现象,如Registry接口其实真正的执行者为DefaultListableBeanFactory.

总结

上一篇下一篇

猜你喜欢

热点阅读