SpringBoot2 Event publisher

igxiaoshan Lv5

SpringBoot Event Publisher

ApplicationContextApplicationEventPublisher都是作为event事件的发布方法,他们之间的区别在哪呢

1
2
ApplicationContext.publishEvent方法和ApplicationEventPublisher方法的功能都是一样的,都是用于发布事件.
在Spring框架中,ApplicationContext接口扩展了ApplicationEventPublisher接口,因此它包含了ApplicationEventPublisher的所有方法,包括publishEvent方法.

扩展ApplicationEventPublisher的使用

  • ApplicationEventPublisher.publishEvent发布事件范围

Spring矿建中,事件发布的范围通常是整个程序上下文.

当调用了ApplicationEventPublisher.publishEvent方法时,事件会被传播到所有注册的监听器

这是因为Spring的事件机制是基于观察者模式实现的,所有对应事件类型的监听器都会收到事件通知

  • 使用自定义的时间类型: 创建自定义的事件类型,并只在特定的地方注册对该事件类型的监听器. 这样,当你发布该事件时,只有注册了对应监听器的地方才会收到通知
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
// Event事件
public class MyCustomEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public MyCustomEvent(Object source) {
super(source);
}
}

// Event监听者
@Component
public class MyCustomEventListener {

@Async
@EventListener
public void handleSyncEvent(MyCustomEvent event) {
System.out.println("Handling custom event end");
}
}

// 这样,只有注册了 MyCustomEventListener 的地方才会收到 MyCustomEvent 事件的通知
  • 使用条件触发: 在监听器方法上使用条件注解,以便满足特定条件时候才处理事件
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class MyCustomEventListener {

@Async
@EventListener
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
public void handleSyncEvent(MyCustomEvent event) {
System.out.println("Handling custom event end");
}
}

// 这样,只有当属性 my.feature.enabled 的值为 true 时,监听器才会处理事件
  • ApplicationEventPublisher.publishEvent发布事件的异步性

同步事件可能导致发布事件的线程在所有监听器完成前被阻塞

而异步事件,发布事件的线程不会等待所有监听器执行完毕,而是继续执行后续操作;监听器的执行将在后台线程中进行,从而不影响发布事件的线程

  • 使用@Async注解: 在监听事件的方法上添加@Async注解,使其成为异步方法

  • 配置异步支持: 确保在配置类或配置文件中启用了异步支持,可以通过@EnableAsync注解或者配制文件中相关配置来实现

    • 如果需要自定义异步执行配置,可以在application.properties或者application.yml中添加相应的配置属性

      1
      2
      3
      4
      ## 配置异步执行线程池核心线程数
      spring.task.execution.pool.core-size=5

      ### ..等等一些配置

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
30
31
32
33
public class MyEvent extends ApplicationEvent {

private String message;

/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public MyEvent(Object source, String message) {
super(source);
this.message = message;
}

public String getMessage() {
return message;
}
}

// 异步事件
public class MySyncEvent extends MyEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
* @param message
*/
public MySyncEvent(Object source, String message) {
super(source, message);
}
}

事件发布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class MyEventPublisher implements ApplicationEventPublisherAware {

private ApplicationEventPublisher eventPublisher;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}

// 异步发布事件
public void publishAsyncEvent(String message) {
MyAsyncEvent myAsyncEvent = new MyAsyncEvent(this, message);
eventPublisher.publishEvent(myAsyncEvent);
System.out.println("After publishing asynchronous event");
}
}

// 后续可以自己改造成 spring 容器管理 Bean注入Event

事件监听

1
2
3
4
5
6
7
8
9
10
11
@Async
@Component
public class MyEventListener {

@Async
@EventListener
public void handleAsyncEvent(MyAsyncEvent event) {
System.out.println("Handling asynchronous event: " + event.getMessage());
}
}

测试案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class ListenerController {

@Autowired
private MyEventPublisher myEventPublisher;

@GetMapping("/asyncPush")
public String asyncPush() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
myEventPublisher.publishAsyncEvent("异步消息");
latch.await(5, TimeUnit.SECONDS);
return "success";
}

}