参考文献

img

img

Java事件/监听器编程模型

  • 设计模式-观察模式扩展
    • 可观者对象(消息发送者): java.util.Observable
    • 观察者: java.util.Observer
  • 标准化接口
    • 事件对象: java.util.EventObject
    • 事件监听器: java.util.EventListener

事件模式中的几个概念

  • 事件源: 事件的触发者.
  • 事件: 描述发生了什么事情的对象.
  • 事件监听器: 监听到事件发生的时候,做一些处理.

硬编码方式实现Spring事件

  • 定义事件

    • 自定义事件,需要继承ApplicationEvent
  • 定义监听器

    • 自定义事件监听器,需要实现ApplicationListener接口,这个接口有个方法onApplicationEvent需要实现,用来处理感兴趣的事件.
  • 创建事件广播器

    • 创建事件广播器ApplicationEventMulticaster,这是个接口,可以自己实现这个接口,也可以直接使用系统提供的SimpleApplicationEventMulticaster
  • 向广播器中注册事件监听器

    • 将事件监听器注册到广播器ApplicationEventMulticaster

      1
      2
      ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();
      applicationEventMulticaster.addApplicationListener(new CustomApplicationListener());
  • 通过广播器发布事件

    • 广播事件,调用ApplicationEventMulticaster#multicastEvent方法广播事件,此时广播器中对这个感兴趣的监听器会处理这个事件.

      1
      applicationEventMulticaster.multicastEvent(new CustomEvent(applicationEventMulticaster,1L));

结合Spring使用Spring事件

  • 面向接口的方式
  • 面向@EventListener注解的方式

面向接口的方式使用步骤

  • 自定义事件,继承ApplicationEvent

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import lombok.Getter;
    import org.springframework.context.ApplicationEvent;

    /**
    * @author HoleLin
    */
    @Getter
    public class PullTaskProcessingEvent extends ApplicationEvent {
    private final String taskId;

    public PullTaskProcessingEvent(String taskId) {
    super(new Object());
    this.taskId = taskId;
    }
    }
  • 自定义事件监听器,需要实现ApplicationListener接口,这个接口有个方法onApplicationEvent需要实现,用来处理感兴趣的事件.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;

    /**
    * 监听器
    *
    * @author HoleLin
    */
    @Component
    public class PullTaskProcessingListener implements ApplicationListener<PullTaskProcessingEvent> {

    @Override
    public void onApplicationEvent(PullTaskProcessingEvent processingEvent) {

    }
    }

  • 在对应的业务处理类中实现ApplicationEventPublishAware,注入ApplicationEventPublisher来发布事件

    1
    this.applicationEventPublisher.publishEvent(new CustomEvent(object));
原理
  • Spring容器在创建Bean的过程中,会判断Bean是否为ApplicationListener类型,进而会将其作为监听器注册到org.springframework.context.support.AbstractApplicationContext#applicationEventMulticaster中.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitialization
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof ApplicationListener) {
    // potentially not detected as a listener by getBeanNamesForType retrieval
    Boolean flag = this.singletonNames.get(beanName);
    if (Boolean.TRUE.equals(flag)) {
    // singleton bean (top-level or inner): register on the fly
    this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
    }
    else if (Boolean.FALSE.equals(flag)) {
    if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
    // inner bean with other scope - can't reliably process events
    logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
    "but is not reachable for event multicasting by its containing ApplicationContext " +
    "because it does not have singleton scope. Only top-level listener beans are allowed " +
    "to be of non-singleton scope.");
    }
    this.singletonNames.remove(beanName);
    }
    }
    return bean;
    }

面向@EventListener注解方式使用步骤

  • 自定义事件,继承ApplicationEvent

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import lombok.Getter;
    import org.springframework.context.ApplicationEvent;

    /**
    * @author HoleLin
    */
    @Getter
    public class PullTaskProcessingEvent extends ApplicationEvent {
    private final String taskId;

    public PullTaskProcessingEvent(String taskId) {
    super(new Object());
    this.taskId = taskId;
    }
    }
  • 使用@EventListener标注在一个Bean的方法上,这个方法就可以用来处理感兴趣的事件

    1
    2
    3
    4
    5
    6
    7
    @Component
    public class CustomListener{
    @EventListener
    public void custom(CustomEvent event){
    // deal event
    }
    }
原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
/**
* Register all relevant annotation post processors in the given registry.
* @param registry the registry to operate on
* @param source the configuration source element (already extracted)
* that this registration was triggered from. May be {@code null}.
* @return a Set of BeanDefinitionHolders, containing all bean definitions
* that have actually been registered by this call
*/
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// ...略

// EventListenerMethodProcessor是@EventListener方法的生命周期处理器
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}

// DefaultEventListenerFactory是@EventListener方法与ApplicationListener适配器的工程类
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}

return beanDefs;
}
  • EventListenerMethodProcessor实现了SmartInitializingSingleton接口,SmartInitializingSingleton接口中的 afterSingletonsInstantiated方法会在所有单例的bean创建完成之后被Spring容器调用

  • Spring中处理@EventListener注解源码位于

    1
    org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    private void processBean(final String beanName, final Class<?> targetType) {
    if (!this.nonAnnotatedClasses.contains(targetType) &&
    AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
    !isSpringContainerClass(targetType)) {

    Map<Method, EventListener> annotatedMethods = null;
    try {
    annotatedMethods = MethodIntrospector.selectMethods(targetType,
    (MethodIntrospector.MetadataLookup<EventListener>) method ->
    AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
    }
    catch (Throwable ex) {
    // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
    if (logger.isDebugEnabled()) {
    logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
    }
    }

    if (CollectionUtils.isEmpty(annotatedMethods)) {
    this.nonAnnotatedClasses.add(targetType);
    if (logger.isTraceEnabled()) {
    logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
    }
    }
    else {
    // Non-empty set of methods
    ConfigurableApplicationContext context = this.applicationContext;
    Assert.state(context != null, "No ApplicationContext set");
    List<EventListenerFactory> factories = this.eventListenerFactories;
    Assert.state(factories != null, "EventListenerFactory List not initialized");
    for (Method method : annotatedMethods.keySet()) {
    for (EventListenerFactory factory : factories) {
    if (factory.supportsMethod(method)) {
    Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
    ApplicationListener<?> applicationListener =
    factory.createApplicationListener(beanName, targetType, methodToUse);
    if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
    }
    context.addApplicationListener(applicationListener);
    break;
    }
    }
    }
    if (logger.isDebugEnabled()) {
    logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
    beanName + "': " + annotatedMethods);
    }
    }
    }
    }
  • @EventListener方法必须是Spring Bean中的public方法,并支持返回类型为非void的情况,当它监听一个或多个ApplicationEvent时,其参数可为零到一个ApplicationEvent

面向接口的事件/监听器设计模式

  • 事件/监听器场景举例

    Java 技术规范 事件接口 监听器接口
    Java Beans java.beans.PropertyChangeEvent java.beans.PropertyChangeListener
    Java AWT java.awt.event.MouseEvent java.awt.event.MouseListener
    Java Swing javax.swing.event.MenuEvent javax.swing.event.MenuListener
    Java Perference java.util.prefs.PreferenceChangeEvent java.util.prefs.PreferenceChangeListener
    Java 技术规范 事件注解 监听器注解
    Servlet 3.0+ @javax.servlet.annotation.WebListener
    JPA 1.0+ @javax.persistence.PostPersist
    Java Common @PostConstruct
    EJB 3.0+ @javax.ejb.PerPassivate
    JSF 2.0+ @javax.faces.event.ListenerFor

Spring 标准事件 - ApplicationEvent

  • Java标准事件java.util.EventObject扩展
    • 扩展特性: 事件发生事件戳
  • Spring 应用上下文ApplicationEvent扩展: ApplicationContextEvent
    • Spring 应用上下文(ApplicationContext)作为事件源
    • 具体实现
      • org.springframework.context.event.ContextClosedEvent
      • org.springframework.context.event.ContextRefreshedEvent
      • org.springframework.context.event.ContextStartedEvent
      • org.springframework.context.event.ContextStoppedEvent

基于接口的Spring事件监听器

  • Java标准事件监听器java.util.EventListener扩展

    • 扩展接口: org.springframework.context.ApplicationListener
    • 设计特点: 单一类型事件处理
    • 处理方法: onApplicationEvent(ApplicationEventevent)
    • 事件类型: org.springframework.context.ApplicationEvent
  • Spring注解: org.springframework.context.event.EventListener

    特性 说明
    设计特点 支持多ApplicationEvent类型,无需接口约束
    注解目标 方法
    是否支持异步执行 支持
    是否支持泛型类型事件 支持
    是指支持顺序控制 支持,配合@Order注解控制

注册Spring ApplicationListener

  • 方法一: ApplicationListener作为Spring Bean注册
  • 方法二: 通过ConfigurableApplicationContextAPI注册

Spring事件发布器

  • 方法一: 通过ApplicationEventPublisher发布Spring事件

    • 获取ApplicationEventPublisher
      • 依赖注入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @SpringBootTest
    class SpirngEventApplicationTests {
    @Autowired
    ApplicationEventPublisher appEventPublisher;
    @Test
    void contextLoads() {
    appEventPublisher.publishEvent(new CustomEvent("1111111"));
    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service
    public class PublisherService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.applicationEventPublisher = applicationEventPublisher;
    }
    }
  • 方法二: 通过ApplicationEventMulticaster发布Spring事件

    • 获取ApplicationEventMulticaster
      • 依赖注入
      • 依赖查找

Spring 层次性上下文事件传播

  • 发生说明

    当Spring应用出现多层次Spring应用上下文(ApplicationContext)时,如Spring WebMVC , Spring BootSpring Cloud场景下,由子ApplicationContext发起Spring事件可能传递其Parent ApplicationContext(直到Root)的过程

  • 如何避免

    • 定位Spring事件源(ApplicationContext)进行过滤处理

同步和异步Spring事件广播

  • 基于实现类: org.springframework.context.event.SimpleApplicationEventMulticaster

    • 模式切换: setTaskExecutor(@Nullable Executor taskExecutor)方法
      • 默认模式: 同步
      • 异步模式: java.util.concurrent.ThreadPoolExecutor
    • 设计缺陷: 非基于接口契约编程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Configuration
    public class MainConfig {
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
    //创建一个事件广播器
    SimpleApplicationEventMulticaster result = new
    SimpleApplicationEventMulticaster();
    //给广播器提供一个线程池,通过这个线程池来调用事件监听器
    Executor executor =
    this.applicationEventMulticasterThreadPool().getObject();
    //设置异步执行器
    result.setTaskExecutor(executor);
    return result;
    }

    @Bean
    public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
    ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
    result.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
    result.setCorePoolSize(5);
    return result;
    }
    }
  • 基于注解: org.springframework.context.event.EventListener

    • 模式切换
      • 默认模式: 同步
      • 异步模式: org.springframework.scheduling.annotation.Async
    • 实现限制: 无法直接实现同步/异步动态切换

Spring 4.1事件异常处理

  • Spring 3.0 错误处理接口: org.springframework.util.ErrorHandler
    • 使用场景
      • Spring事件(Events)
        • SimpleApplicationEventMulticaster Spring 4.1开始支持
      • Spring本地调度(Scheduling)
        • org.springframework.scheduling.concurrent.ConcurrentTaskScheduler
        • org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

Spring事件/监听实现原理

  • 核心类: org.springframework.context.event.SimpleApplicationEventMulticaster
    • 设计模式: 观察者模式扩展
      • 被观察者: org.springframework.context.ApplicationListener
        • API 添加
        • 依赖查找
      • 通过对象: org.springframework.context.ApplicationEvent
    • 执行模式: 同步/异步
    • 异常处理: org.springframework.util.ErrorHandler
    • 泛型处理: org.springframework.core.ResolvableType
  • SimpleApplicationEventMulticaster默认采用同步广播事件的方式.Spring事件通过调用SimpleApplicationEventMulticaster#multicastEvent方法广播,根据ApplicationEvent具体类型查找匹配的ApplicationListener列表,然后逐一同步或异步地调用ApplicationListener.onApplicationEvent(ApplicationEvent event)方法,实现ApplicationListener事件监听.

泛型事件

  • 实现org.springframework.core.ResolvableTypeProvider接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import lombok.Getter;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;

public class GenericsEvent<T> implements ResolvableTypeProvider {

@Getter
private T source;

@Getter
private GenericsType type;

public GenericsEvent(T data,GenericsType type) {
this.source = data;
this.type = type;
}

@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(),
ResolvableType.forInstance(source));
}
}

// in some method
// publisher.publishEvent(new GenericsEvent<>(createUser,GenericsType.CREATE));
// publisher.publishEvent(new GenericsEvent<>(deletedUser,GenericsType.DELETE));

SpringBoot事件

监听方法 refresh()方法执行顺序 事件类型 发生时机 SpringBoot起始版本
starting() 调用之前 ApplicationStartingEvent SpringBoot应用已启动时 1.5
environmentPrepared() 调用之前 ApplicationEnvironmentPreparedEvent SpringBoot Environment实例已准备时 1.0
contextPrepared() 调用之前 ApplicationContextInitializedEvent applicationcontextinitializer被调用时发布,但在加载任何bean定义之前 2.1
contextLoaded() 调用之前 ApplicationPreparedEvent SpringBoot Environment应用预备时 1.0
started() 调用之后 ApplicationStartedEvent SpringBoot应用已启动时
ready() 调用之后 ApplicationReadyEvent SpringBoot Environment应用完全可用时 1.3
failed() 调用之后 ApplicationFailedEvent SpringBoot Environment应用启动失败时 1.0
  • SpringBoot1.4开始采用SpringApplicationConfigurableApplicationContext隔离SimpleApplicationEventMulticaster实例,即SpringBoot有单独的SimpleApplicationEventMulticaster.
  • SpringBoot事件/监听机制继承与Spring事件/监听机制,其事件类型继承与Spring ApplicationEvent,事件监听器仍通过ApplicationListener实现,而广播器实现SimpleApplicationEventMulticaster将TM关联起来.

SpringCloud事件

事件类型 发生时机
EnvironmentChangeEvent Environment示例配置属性发生变化时
HeartbeatEvent DiscoveryClient客户端发送心跳时
InstancePreRegisteredEvent 当服务实例注册前
InstanceRegisteredEvent 当服务实例注册后
RefreshEvent RefreshEndpoint被调用时
RefreshScopeRefreshedEvent Refresh Scope Bean被调用时