https://www.baeldung.com/spring-events

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

 

1. Overview

In this article, we’ll be discussing how to use events in Spring.

Events are one of the more overlooked functionalities in the framework but also one of the more useful. And – like many other things in Spring – event publishing is one of the capabilities provided by ApplicationContext.

There are a few simple guidelines to follow:

  • the event should extend ApplicationEvent
  • the publisher should inject an ApplicationEventPublisher object
  • the listener should implement the ApplicationListener interface

2. A Custom Event

Spring allows to create and publish custom events which – by default – are synchronous. This has a few advantages – such as, for example, the listener being able to participate in the publisher’s transaction context.

2.1. A Simple Application Event

Let’s create a simple event class – just a placeholder to store the event data. In this case, the event class holds a String message:

1
2
3
4
5
6
7
8
9
10
11
public class CustomSpringEvent extends ApplicationEvent {
    private String message;
 
    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}

2.2. A Publisher

Now let’s create a publisher of that event. The publisher constructs the event object and publishes it to anyone who’s listening.

To publish the event, the publisher can simply inject the ApplicationEventPublisher and use the publishEvent() API:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
 
    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

Alternatively, the publisher class can implement the ApplicationEventPublisherAware interface – this will also inject the event publisher on the application start-up. Usually, it’s simpler to just inject the publisher with @Autowire.

2.3. A Listener

Finally, let’s create the listener.

The only requirement for the listener is to be a bean and implement ApplicationListener interface:

1
2
3
4
5
6
7
@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

Notice how our custom listener is parametrized with the generic type of custom event – which makes the onApplicationEvent() method type safe. This also avoids having to check if the object is an instance of a specific event class and casting it.

And, as already discussed – by default spring events are synchronous – the doStuffAndPublishAnEvent()method blocks until all listeners finish processing the event.

3. Creating Asynchronous Events

In some cases, publishing events synchronously isn’t really what we’re looking for – we may need async handling of our events.

You can turn that on in the configuration by creating an ApplicationEventMulticaster bean with an executor; for our purposes here SimpleAsyncTaskExecutor works well:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster
          = new SimpleApplicationEventMulticaster();
         
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

The event, the publisher and the listener implementations remain the same as before – but now, the listener will asynchronously deal with the event in a separate thread.

4. Existing Framework Events

Spring itself publishes a variety of events out of the box. For example, the ApplicationContext will fire various framework events. E.g. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent etc.

These events provide application developers an option to hook into the lifecycle of the application and the context and add in their own custom logic where needed.

Here’s a quick example of a listener listening for context refreshes:

1
2
3
4
5
6
7
public class ContextRefreshedListener
  implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

To learn more about existing framework events, have a look at our next tutorial here.

5. Annotation-Driven Event Listener

Starting with Spring 4.2, an event listener is not required to be a bean implementing the ApplicationListener interface – it can be registered on any public method of a managed bean via the @EventListener annotation:

1
2
3
4
5
6
7
8
@Component
public class AnnotationDrivenContextStartedListener {
    // @Async
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event.");
    }
}

As before, the method signature declares the event type it consumes. As before, this listener is invoked synchronously. But now making it asynchronous is as simple as adding an @Async annotation (do not forget to enable Async support in the application).

6. Generics Support

It is also possible to dispatch events with generics information in the event type.

6.1. A Generic Application Event

Let’s create a generic event type. In our example, the event class holds any content and a successstatus indicator:

1
2
3
4
5
6
7
8
9
10
public class GenericSpringEvent<T> {
    private T what;
    protected boolean success;
 
    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}

Notice the difference between GenericSpringEvent and CustomSpringEvent. We now have the flexibility to publish any arbitrary event and it’s not required to extend from ApplicationEvent anymore.

6.2. A Listener

Now let’s create a listener of that event. We could define the listener by implementing the ApplicationListener interface like before:

1
2
3
4
5
6
7
@Component
public class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) {
        System.out.println("Received spring generic event - " + event.getWhat());
    }
}

But unfortunately, this definition requires us to inherit GenericSpringEvent from the ApplicationEventclass. So for this tutorial, let’s make use of an annotation-driven event listener discussed previously.

It is also possible to make the event listener conditional by defining a boolean SpEL expression on the @EventListener annotation. In this case, the event handler will only be invoked for a successful GenericSpringEvent of String:

1
2
3
4
5
6
7
@Component
public class AnnotationDrivenEventListener {
    @EventListener(condition = "#event.success")
    public void handleSuccessful(GenericSpringEvent<String> event) {
        System.out.println("Handling generic event (conditional).");
    }
}

The Spring Expression Language (SpEL) is a powerful expression language that’s covered in details in another tutorial.

6.3. A Publisher

The event publisher is similar to the one described above. But due to type erasure, we need to publish an event that resolves the generics parameter we would filter on. For example, class GenericStringSpringEvent extends GenericSpringEvent<String>.

And there’s an alternative way of publishing events. If we return a non-null value from a method annotated with @EventListener as the result, Spring Framework will send that result as a new event for us. Moreover, we can publish multiple new events by returning them in a collection as the result of event processing.

7. Transaction Bound Events

This paragraph is about using the @TransactionalEventListener annotation. To learn more about transaction management check out the Transactions with Spring and JPA tutorial.

Since Spring 4.2, the framework provides a new @TransactionalEventListener annotation, which is an extension of @EventListener, that allows binding the listener of an event to a phase of the transaction. Binding is possible to the following transaction phases:

  • AFTER_COMMIT (default) is used to fire the event if the transaction has completed successfully
  • AFTER_ROLLBACK – if the transaction has rolled back
  • AFTER_COMPLETION – if the transaction has completed (an alias for AFTER_COMMITand AFTER_ROLLBACK)
  • BEFORE_COMMIT is used to fire the event right before transaction commit

Here’s a quick example of transactional event listener:

1
2
3
4
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}

This listener will be invoked only if there’s a transaction in which the event producer is running and it’s about to be committed.

And, if no transaction is running the event isn’t sent at all unless we override this by setting fallbackExecution attribute to true.

8. Conclusion

In this quick tutorial, we went over the basics of dealing with events in Spring – creating a simple custom event, publishing it, and then handling it in a listener.

We also had a brief look at how to enable asynchronous processing of events in the configuration.

Then we learned about improvements introduced in Spring 4.2, such as annotation-driven listeners, better generics support, and events binding to transaction phases.

As always, the code presented in this article is available over on Github. This is a Maven based project, so it should be easy to import and run as it is.

Spring Events的更多相关文章

  1. Spring Boot(二)Application events and listeners

    一.自定义监听器: 1.创建: META-INF/spring.factories 2.添加: org.springframework.context.ApplicationListener=com. ...

  2. spring源码分析之context

    重点类: 1.ApplicationContext是核心接口,它为一个应用提供了环境配置.当应用在运行时ApplicationContext是只读的,但你可以在该接口的实现中来支持reload功能. ...

  3. spring mvc DispatcherServlet详解之前传---FrameworkServlet

    做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...

  4. Spring ApplicationContext 简解

    ApplicationContext是对BeanFactory的扩展,实现BeanFactory的所有功能,并添加了事件传播,国际化,资源文件处理等.   configure locations:(C ...

  5. [spring源码学习]七、IOC源码-Context

    一.代码实例 如之前介绍的,spring中ioc是它最为核心的模块,前边花了大量时间分析spring的bean工厂和他如何生成bean,可是在我们实际应用中,很少直接使用beanFactory,因为s ...

  6. Spring 4 + Reactor Integration Example--转

    原文地址:http://www.concretepage.com/spring-4/spring-4-reactor-integration-example Reactor is a framewor ...

  7. (转载)Spring的refresh()方法相关异常

    如果是经常使用Spring,特别有自己新建ApplicationContext对象的经历的人,肯定见过这么几条异常消息:1.LifecycleProcessor not initialized - c ...

  8. Spring学习笔记之二----基于XML的Spring AOP配置

    在Spring配置文件中,通常使用<aop:config>元素来设置AOP,其中应包括: <aop:aspect>指定aspect,aspect是一个POJO类,包含了很多的a ...

  9. spring bean生命周期管理--转

    Life Cycle Management of a Spring Bean 原文地址:http://javabeat.net/life-cycle-management-of-a-spring-be ...

随机推荐

  1. 浅谈Semaphore类-示例

    Semaphore类有两个重要方法 1.semaphore.acquire(); 请求一个信号量,这时候信号量个数-1,当减少到0的时候,下一次acquire不会再执行,只有当执行一个release( ...

  2. DP之背包

    一.01背包: (以下均可用一维来写 即只能选择一次的物品装在一定容积的背包中.f[i][j]表示前i件物品在容积为j时的最大价值. for(int i = 1; i <= n ;  i++){ ...

  3. rabbitMq 学习笔记(一)

    消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题 实现高性能,高可用,可伸缩和最终一致性架构. RabbitMQ 是采用 Erlang 语言实现 AMQP (Adva ...

  4. webpack详细介绍以及配置文件属性!

    1.webpack简单介绍 (1)webpack是一个用于实现前端模块化开发工具,可帮助我们自动打包编译成浏览器能够识别的代码 :同时支持commonjs规范 以及es6的import规范: 同时具备 ...

  5. web安全常用工具

    简单工具:明小子,阿d注入工具,namp,穿山甲,御剑,旁注 漏洞扫描工具:appscan .awvs.nbsi 端口扫描工具:nessus.namp.天镜脆弱性扫描与管理系统 数据库备份工具:中国菜 ...

  6. js中this绑定方式及如何改变this指向

    this的绑定方式基本有以下几种: 隐式绑定 显式绑定 new 绑定 window 绑定 箭头函数绑定 隐式绑定 第一个也是最常见的规则称为 隐式绑定. var a = { str: 'hello', ...

  7. c# 导出2007格式的Excel的连接字符串

    上次做了个导出excel文件的客户端软件,没有注意到:当打开2007版的excel时提示错误“外部表不是预期的格式”,刚才网上荡了点资料,改了一下连接字符串,问题解决了: 把:string strCo ...

  8. Linux C/C++编译过程中的各种not declared in this scope

    Linux C/C++编译时经常会"XXX was not declared in this scope" 原因可能是以下几种: 变量名或函数名写错了; 忘记定义了 没有成功链接到 ...

  9. OpenSSL生成私钥和公钥

    1.生成私钥 -- 生成 RSA 私钥(传统格式的) openssl genrsa -out rsa_private_key.pem 1024 -- 将传统格式的私钥转换成 PKCS#8 格式的(JA ...

  10. 01、Linux基础命令

    linux 一些主要目录的认识: /bin 二进制可执行命令 /boot 存放系统引导文件,如 内核.grub 等 /dev 设备文件 /etc 系统配置目录 /home 普通用户家目录 /lib 系 ...