Spring Boot 的事件发布与监听

事件监听机制我理解的应该是类似于一种观察者模式。利用事件的监听机制可以实现业务的发起与业务的处理解耦。说白了更能体现 单依职责原则 想象一下如果一个用户触发了某个动作需要发送邮件, 那么发出动作的一方一定是在 用户相关的类, 发送邮件的动作一定是在邮件相关的类, 这种情况下用户类就需要依赖邮件类, 形成耦合. 如果用事件来解决这个问题, 用户只需要发起一个相关的事件, 至于谁来处理这个事件, 用户类不需要关心, 而邮件类只需要监听这个事件, 至于谁发起的这个事件, 邮件类也不需要关心. 单依职责原则, 最少知识原则

基于 Spring 实现事件发布与监听需要三步:

  • 定义事件
  • 发布事件
  • 监听事件

定义事件

  1. 继承 ApplicationEvent 定义事件类

新建一个类,随便起个名字,最好是 xxxEvent 这种命名。然后继承 ApplicationEvent 这个类即可

public class CrewEvent extends ApplicationEvent {

    @Getter
    private final String name;

    /**
     * 创建一个新的 {@code ApplicationEvent}.
     *
     * @param source 事件源
     * @param name 自定义的属性,根据自己的业务需要添加
     */
    public CrewEvent(Object source, String name) {
        super(source);
        this.name = name;
    }
}
  1. Spring 4.2.x 及以后的版本可以不用继承 ApplicationEvent 类, 任何一个普通的 Java Bean 都可以当做 事件类
public class CrewEvent {

    @Getter
    private final String name;
    
    @Getter
    private final Object source;

    /**
     * 创建一个新的 {@code ApplicationEvent}.
     *
     * @param source 事件源
     * @param name 自定义的属性,根据自己的业务需要添加
     */
    public CrewEvent(Object source, String name) {
        this.name = name;
        this.source = source;
    }
}

发布事件

发布事件只需要调用 ApplicationEventPublisherpublishEvent 方法即可

@RestController
@AllArgsConstructor
@Slf4j
public class ReportController {

    private final ApplicationEventPublisher applicationEventPublisher;

    @GetMapping("/report")
    public String getView() {
        applicationEventPublisher.publishEvent(new CrewEvent(this, "report"));
        return "事件已经触发";
    }
}

事件监听

  1. 基于注解监听

Spring 提供了基于注解的事件监听方法只需要在 Spring Bean 的方法上加上 @EventListener 即可.

EventListener 可以传入参数来判断被修饰的方法监听哪个事件类

还可以添加 @Async 注解, 使其可以被异步的执行

@Component
@Slf4j
public class CrewEventListener {

    @Async
    @EventListener(CrewEvent.class)
    public void crewEventHandle(CrewEvent event) {
        log.info("event.name: {}", event.getName());
        log.info("event.source: {}", event.getSource());
    }
}

  1. 基于接口监听

基于接口的监听需要实现ApplicationListener 接口在泛型参数中填入要监听的事件类, 并实现 onApplicationEvent 方法即可

接口泛型中的时间类会被当做参数传递给 onApplicationEvent 方法

@Component
@Slf4j
public class CrewEventListener implements ApplicationListener<CrewEvent> {

    @Override
    public void onApplicationEvent(CrewEvent event) {
        log.info("event.name: {}", event.getName());
        log.info("event.source: {}", event.getSource());
        System.out.println(event.getTimestamp());
    }
}

Spring Boot 的启动事件

Spring Boot 在启动的过程中也会发布一系列的事件, 例如: 在应用启动且未作任何处理时会发布ApplicationStartingEvent 事件, 在确定Spring Boot应用使用的 Environment 且 context 创建之前发送ApplicationEnvironmentPreparedEvent事件. 对这些事件进行监听可以实现一些特殊的效果, 比如在程序启动时做一些启动一些工作线程, 加载一些系统参数等.

下面进行详细说明

Spring Boot 启动会触发哪些事件

ApplicationStartingEvent :应用启动且未作任何处理(除listener注册和初始化)
ApplicationEnvironmentPreparedEvent: 确定springboot应用使用的Environment且context创建之前
ApplicationPreparedEvent:context已经创建且没有refresh发送个事件
ApplicationStartedEvent: context已经refresh且application and command-line runners(如果有)调用之前发送这个事件
ApplicationReadyEvent: pplication and command-line runners (如果有)执行完后发送这个事件,此时应用已经启动完毕
ApplicationFailedEvent: 应用启动失败后产生这个事件

监听Spring Boot 启动事件

public class CrewSystemEventListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        //springboot应用启动且未作任何处理(除listener注册和初始化)的时候发送ApplicationStartingEvent
        if (event instanceof ApplicationStartingEvent) {
            System.out.println("ApplicationStarting");
        }
        //确定springboot应用使用的Environment且context创建之前发送这个事件
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            System.out.println("ApplicationEnvironmentPrepared");
        }
        //context已经创建且没有refresh发送个事件
        if (event instanceof ApplicationPreparedEvent) {
            System.out.println("ApplicationPrepared");
        }
        //context已经refresh且application and command-line runners(如果有) 调用之前发送这个事件
        if (event instanceof ApplicationStartedEvent) {
            System.out.println("ApplicationStarted");
        }
        //application and command-line runners (如果有)执行完后发送这个事件,此时应用已经启动完毕
        if (event instanceof ApplicationReadyEvent) {
            // 此时,Spring 容器构建完成了,已经可以从中拿到 Bean 了
            ApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext();
            SystemInitManager initService = context.getBean(SystemInitManager.class);
            initService.doSystemInit();
        }
        //应用启动失败后产生这个事件
        if (event instanceof ApplicationFailedEvent) {
            System.out.println("ApplicationFailed");
        }
    }
}

由于我们要监听 Spring Boot 的启动过程, 那就需要在 Spring Boot 程序刚启动时就执行, 那么就两种方式了

  1. main函数
@SpringBootApplication
public class CrewWarApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .sources(CrewWarApplication.class)
                .listeners(new CrewSystemEventListener())
                .run(args);
    }
}
  1. spring.factories

在resources 下新建 META-INF 文件夹,在里面新建 spring.factories文件, 在里面写入下面的代码段

众所周知, Spring Boot 程序一旦启动最先扫描的就是这玩意儿.

org.springframework.context.ApplicationListener=\
com.captain.springboot.war.event.CrewSystemEventListener

ps. 上面的示例代码为了验证在最后阶段 Spring 容器中的内容,用到了 SystemInitManager 这玩意儿是我自己写的,在线面贴出来

public interface SystemInitManager {

    /**
     * 系统初始化
     */
    void doSystemInit();
}

@Component
@Slf4j
public class CrewSystemInitManager implements SystemInitManager {

    @Override
    public void doSystemInit() {
        log.info("系统初始化完成,你现在可以从 springContext 中获取 Bean");
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值