Spring 事件

简介

这篇文章我们来讲解Spring中如何使用事件。

Spring中使用事件,需要遵守一些规则 :

  • 使用以下两种方式之一定义事件
    • 继承自 ApplicationEvent
    • 不继承自ApplicationEvent
  • 使用以下两种方式之一定义事件发布者
    • 注入一个ApplicationEventPublisher对象到事件发布者
    • 事件发布者实现接口ApplicationEventPublisherAware
  • 使用如下两种方式之一实现事件监听器
    • 实现接口ApplicationListener
    • 使用注解@EventListener

自定义一个事件

Spring允许创建和发布自定义事件,并且缺省情况下,这些事件的处理逻辑的执行都是同步的。这样做有一些优点,比如,监听器中的事件处理逻辑能够参与到事件发布者的事务上下文中来。

注意:这里同步也就意味着事件处理逻辑在同一个线程中发起,执行也会发生在当前线程中(但是如果事件处理逻辑中开发人员发起了其它异步调用,就不属于这种情况了)。

一个简单的应用事件

让我们来创建一个简单的事件类,该事件只携带消息来源对象信息和一条消息。

方法一:继承自 ApplicationEvent

import org.springframework.context.ApplicationEvent;
public class CustomApplicationEvent extends ApplicationEvent {
    private String message;

    public CustomApplicationEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

方法二:不继承自 ApplicationEvent

public class CustomEvent {
    private String message;
    Object source;

    public CustomEvent(Object source, String message) {
        this.source = source;
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

事件发布者

现在让我们创建上面事件的一个发布者。这个事件发布者构造事件对象并将事件发布出去。

要发布事件,发布者可以简单地注入ApplicationEventPublisher然后调用其事件发布API方法publishEvent() :

public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
 
    /**
     * 发布两个事件:
     * 1. 继承自 Spring ApplicationEvent 的事件
     * 2. 不继承自 Spring ApplicationEvent  的任意Java对象事件
     *
     * @param message 事件中所要携带的消息
     */
    public void publishEvent(final String message) {
        System.out.println("Publishing custom ApplicationEvent . ");
        CustomApplicationEvent customApplicationEvent = 
	        new CustomApplicationEvent(this, message);
        applicationEventPublisher.publishEvent(customApplicationEvent);//发布事件

        System.out.println("Publishing custom common event . ");
        CustomEvent customEvent = new CustomEvent(this, message);
        applicationEventPublisher.publishEvent(customEvent);//发布事件
    }
}

除了上面这种方法之外,还有一种方法是发布者类实现接口ApplicationEventPublisherAware,实现了该接口的话应用程序启动时会向该事件发布者bean注入事件发布器ApplicationEventPublisher。不过通常情况下,使用@Autowire注解方式注入事件发布器更简单。

事件监听器

最后让我们创建事件监听器。

ApplicationEvent子类事件监听器

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;

// 该例子中提到的两种监听方法可以分开独立使用,相互之间并无关系,
// 本例子将它们放在同一类中仅仅出于演示其监听同一类型事件的目的
@Component
public class CustomApplicationEventListener 
		implements ApplicationListener<CustomApplicationEvent> {
	// 监听方法一:监听器类继承自ApplicationListener
    @Override
    public void onApplicationEvent(CustomApplicationEvent event) {
        System.out.println(
		        "ApplicationListener -- Received custom spring ApplicationEvent - "
                + event.getMessage());
    }
	// 监听方法二:使用注解@EventListener
    @EventListener
    public void handleCustomApplicationEvent(CustomApplicationEvent event) {
        System.out.println(
		        "@EventListener -- Received custom spring ApplicationEvent - "
                + event.getMessage());
    }
}

请注意我们的自定义事件监听器使用了自定义事件的泛型作为参数,这样就会让事件监听器的方法onApplicationEvent()变成类型安全的。同时也避免了在onApplicationEvent()中检查入参是否是给定的事件类型以及相应的类型转换。

ApplicationEvent子类事件监听器

import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.event.EventListener;

// 该例子中提到的两种监听方法可以分开独立使用,相互之间并无关系,
// 本例子将它们放在同一类中仅仅出于演示其监听同一类型事件的目的
@Component
public class CustomEventListener 
		implements ApplicationListener<PayloadApplicationEvent<CustomEvent>> {
	// 监听方法一:监听器类继承自ApplicationListener
    @Override
    public void onApplicationEvent(PayloadApplicationEvent<CustomEvent> event) {
        System.out.println("ApplicationListener -- Received custom common event - "
                + event.getPayload().getMessage());
    }

	// 监听方法二:使用注解@EventListener
    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("@EventListener -- Received custom common event - "
                + event.getMessage());
    }
}

上面的例子中需要提醒的一点是,如果所发布事件的定义类T不是继承自ApplicationEvent,那么它在内部会被包装成一个PayloadApplicationEvent<T>对象然后发布,但是只有方法onApplicationEvent的参数为PayloadApplicationEvent<T>的监听器才能监听到该事件。

另外,再次强调,缺省情况下,Spring事件是同步执行的,事件发布者的方法publishEvent()会阻塞直到所有监听器处理完所发布的事件(实际上就是在同一个线程中的同步执行)。

创建异步事件

在某些情况下,同步处理并不是我们真正需要的,我们想要异步。

想做到异步,有两种方法:

配置文件中指定异步任务执行器给事件发布者

你可以在配置中使用任务执行器(TaskExecutor)创建一个ApplicationEventMulticaster;我们的例子使用类SimpleAsyncTaskExecutor来演示这一目的 :

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster 
          = new SimpleApplicationEventMulticaster();
         
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

现在,事件,事件发布者和监听器实现都跟之前的一样保持不变,但是监听器中的事件处理逻辑已经会在另外一个线程中处理事件了。

异步注解事件监听器方法

	@Async
	@EventListener
    public void handleCustomSpringEvent(CustomApplicationEvent event) {
       System.out.println("Received spring custom event - " + event.getMessage());
    }

框架自带事件

Spring自身原本就发布了各种各样的事件。比如,ApplicationContext会发布各种框架事件,诸如ContextRefreshedEvent,ContextStartedEvent,RequestHandledEvent之类。

这些事件提供给应用程序开发者一个参与到应用和上下文的生命周期中来的机会,开发者可以在需要的地方加入一些自定义的逻辑。

下面是一个监听应用上下文刷新的事件监听器例子:

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

@Component
public class ContextRefreshedListener 
  implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context refreshed event. ");
    }
}

总结

本文主要讲述了Spring框架自身的事件机制。除了使用Spring框架自身的事件机制,实际上开发人员也可以使用其他第三方的事件机制,比如 Guava 事件总线等。

另外,本文中提到的例子在github上都能找到源代码。

#参考文章
Spring Events
Better application events in Spring Framework 4.2
Spring 4.3 – Event Listener
Spring事件机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值