SpringFramework 的事件驱动核心概念划分为 4 个:事件源、事件、广播器、监听器。
- 事件源: 发布事件的对象
extends ApplicationEvent - 事件: 事件源发布的信息 / 作出的动作
ApplicationEventPublisher.publishEvent(ApplicationEvent event) - 广播器: 事件真正广播给监听器的对象【即 ApplicationContext 】
ApplicationContext 接口有实现 ApplicationEventPublisher 接口,具备事件广播器的发布事件的能力
ApplicationEventMulticaster 组合了所有的监听器,具备事件广播器的广播事件的能力 - 监听器: 监听事件的对象
implements ApplicationListener<ApplicationEvent>
事件与监听器
Spring 中内置的监听器接口是 ApplicationListener ,带了一个泛型,代表要监听的具体事件。
自定义监听器,只需要实现这个 ApplicationListener 接口即可。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
ApplicationEvent: 事件模型的抽象,是一个抽象类。
public abstract class ApplicationEvent extends EventObject
ApplicationContextEvent: 会把 IOC 容器一起传进去。
public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
Spring 中 4 个默认的内置事件:
ContextRefreshedEvent:IOC 容器刷新完毕但尚未启动,所有单实例 Bean 刚创建完成后,就发布的事件。ContextClosedEvent:IOC 容器已经关闭但尚未销毁所有 Bean 。ContextStartedEvent:ContextStoppedEvent:
ContextRefreshedEvent 事件防止重复触发:
对于web应用会出现父子容器,这样就会触发两次。
@Component
public class TestTask implements ApplicationListener<ContextRefreshedEvent> {
private volatile AtomicBoolean isInit = new AtomicBoolean(false);
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//防止重复触发
if(!isInit.compareAndSet(false, true)) {
return;
}
start();
}
private void start() {
//开启任务
System.out.println("****-------------------init---------------******");
}
}
编写监听器: 监听 ContextRefreshedEvent 事件。
@Component
public class ContextRefreshedApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 容器刷新完毕后打印
System.out.println("ContextRefreshedApplicationListener监听到ContextRefreshedEvent事件!");
}
}
注解式监听器:
@Component
public class ContextRefreshedApplicationListener {
@EventListener
public void onContextRefreshedEvent(ContextRefreshedEvent event) {
// 容器刷新完毕后打印
System.out.println("ContextRefreshedApplicationListener监听到ContextRefreshedEvent事件!");
}
}
自定义事件开发
场景概述: 新用户注册成功后,会同时发送短信、邮件、站内信,通知用户注册成功。
用户注册成功后,广播一个用户注册成功的事件,将用户信息带入事件广播出去,发送短信、邮件、站内信的监听器监听到注册成功的事件后,会分别执行不同形式的通知动作。
自定义用户注册成功事件:
// 注册成功的事件
public class RegisterSuccessEvent extends ApplicationEvent {
public RegisterSuccessEvent(Object source) {
super(source);
}
}
分别编写发送短信、发送邮件,和发送站内信的监听器:
@Component
public class SmsSenderListener implements ApplicationListener<RegisterSuccessEvent> {
@Override
public void onApplicationEvent(RegisterSuccessEvent event) {
System.out.println("监听到用户注册成功,发送短信。。。");
}
}
@Component
public class MessageSenderListener {
@EventListener
public void onRegisterSuccess(RegisterSuccessEvent event) {
System.out.println("监听到用户注册成功,发送站内信。。。");
}
}
@Component
public class EmailSenderListener {
@EventListener
public void onRegisterSuccess(RegisterSuccessEvent event) {
System.out.println("监听到用户注册成功!发送邮件中。。。");
}
}
编写注册逻辑业务层: 使用 ApplicationEventPublisher 来发布事件,此处选择使用回调注入的方式。
@Service
public class RegisterService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void register(String username) {
// 用户注册的动作。。。
System.out.println(username + "注册成功。。。");
// 发布事件
applicationEventPublisher.publishEvent(new RegisterSuccessEvent(username));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
编写测试启动类:
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
RegisterService registerService = ctx.getBean(RegisterService.class);
registerService.register("James");
}
James注册成功。。。
监听到用户注册成功!发送邮件中。。。
监听到用户注册成功,发送站内信。。。
监听到用户注册成功,发送短信。。。
结论: 注解式监听器的触发时机比接口式监听器早。
调整监听器的触发顺序: 方法上标注 @Order(1)。
本文介绍Spring框架中的事件驱动机制,包括事件源、事件、广播器和监听器等核心概念,并通过具体示例演示如何实现自定义事件及监听器。
8002

被折叠的 条评论
为什么被折叠?



