Spring原理解析 之事件机制&监听器

本文介绍Spring框架中的事件驱动机制,包括事件源、事件、广播器和监听器等核心概念,并通过具体示例演示如何实现自定义事件及监听器。

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)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值