借助Spring可以非常简单的实现事件监听机制,本文简单介绍下面向接口与注解监听的两种姿势
I. 项目环境
本项目借助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> 复制代码
II. 事件机制
1. 事件对象
在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 + '\'' + '}'; } } 复制代码
2. 接口方式消费
消费事件有两种方式,接口的声明,主要是实现ApplicationListener
接口;注意需要将listener声明为Spring的bean对象
@Service public class MsgEventListener implements ApplicationListener<MsgEvent> { @Override public void onApplicationEvent(MsgEvent event) { System.out.println("receive msg event: " + event); } } 复制代码
3. 注解方式消费
实现接口需要新建实现类,更简单的方法是直接在消费方法上加一个注解@EventListener
@EventListener(MsgEvent.class) public void consumer(MsgEvent msgEvent) { System.out.println("receive msg by @anno: " + msgEvent); } 复制代码
这个注解,支持根据Event参数类型进行匹配,即上面的实例中,方法上直接加@EventListener
不指定圆括号内部的也没关系
4. 发布事件
前面是消费事件,消费的前提是有事件产生,在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)); } } 复制代码
5. 测试
一个简单的测试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上有个相似的问题 stackoverflow.com/questions/3…,这里主要提了一个观点
- 发布消息比事件消费注册的要早
那么是这个原因么? 静待下次源码分析