借助 Spring 可以非常简单的实现事件监听机制,本文简单介绍下面向接口与注解监听的两种姿势
SpringBoot 事件机制的两种消费姿势[1]
本项目借助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
进行开发
为了后面的发布事件验证,起一个 web 服务
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
在 Spring 中,所有的事件需要继承自ApplicationEvent
,一个最基础的MsgEvent
如下
public class MsgEvent extends ApplicationEvent {
private String msg;
/**
* 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 MsgEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
@Override
public String toString() {
return "MsgEvent{" +
"msg='" + msg + '\'' +
'}';
}
}
消费事件有两种方式,接口的声明,主要是实现ApplicationListener
接口;注意需要将 listener 声明为 Spring 的 bean 对象
@Service
public class MsgEventListener implements ApplicationListener<MsgEvent> {
@Override
public void onApplicationEvent(MsgEvent event) {
System.out.println("receive msg event: " + event);
}
}
实现接口需要新建实现类,更简单的方法是直接在消费方法上加一个注解@EventListener
@EventListener(MsgEvent.class)
public void consumer(MsgEvent msgEvent) {
System.out.println("receive msg by @anno: " + msgEvent);
}
这个注解,支持根据 Event 参数类型进行匹配,即上面的实例中,方法上直接加@EventListener
不指定圆括号内部的也没关系
前面是消费事件,消费的前提是有事件产生,在 Spring 中,发布事件主要需要借助ApplicationContext
来实现
@Service
public class MsgPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void publish(String msg) {
applicationContext.publishEvent(new MsgEvent(this, msg));
}
}
一个简单的测试 demo
@RestController
public class IndexController {
@Autowired
private MsgPublisher msgPublisher;
@GetMapping(path = "pub")
public String publish(String msg) {
msgPublisher.publish(msg);
return "ok";
}
}
访问: curl http://localhost:8082/pub?msg=一灰灰blog
输出日志:
receive msg by @anno: MsgEvent{msg='一灰灰blog'}
receive msg event: MsgEvent{msg='一灰灰blog'}
上面这个测试两种消费方式都可以成功,但是,在实测的过程中发现一种 case,注解消费方式不生效,测试姿势如下
@SpringBootApplication
public class Application {
public Application(MsgPublisher msgPublisher) {
msgPublisher.publish("一灰灰blog");
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
直接在启动类的构造方法中发布事件,发现接口方式可以接收事件,但是注解方式不生效,why?
在 stockoverstack 上有个相似的问题 https://stackoverflow.com/questions/38487474/springboot-eventlistener-dont-receive-events[2],这里主要提了一个观点
那么是这个原因么?静待下次源码分析
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
SpringBoot 事件机制的两种消费姿势: https://spring.hhui.top/spring-blog/2021/04/29/210429-SpringBoot%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%B6%88%E8%B4%B9%E5%A7%BF%E5%8A%BF/
[2]https://stackoverflow.com/questions/38487474/springboot-eventlistener-dont-receive-events: https://stackoverflow.com/questions/38487474/springboot-eventlistener-dont-receive-events
[3]https://github.com/liuyueyi/spring-boot-demo: https://github.com/liuyueyi/spring-boot-demo
[4]https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/012-context-listener/: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/012-context-listener/