美文网首页
【SpringBoot 基础系列】事件机制的两种消费姿势

【SpringBoot 基础系列】事件机制的两种消费姿势

作者: 一灰灰blog | 来源:发表于2021-07-02 19:30 被阅读0次
    image

    借助Spring可以非常简单的实现事件监听机制,本文简单介绍下面向接口与注解监听的两种姿势

    【SpringBoot 基础系列】事件机制的两种消费姿势

    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上有个相似的问题 https://stackoverflow.com/questions/38487474/springboot-eventlistener-dont-receive-events,这里主要提了一个观点

    • 发布消息比事件消费注册的要早

    那么是这个原因么? 静待下次源码分析

    II. 其他

    0. 项目

    1. 一灰灰Blog

    尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    相关文章

      网友评论

          本文标题:【SpringBoot 基础系列】事件机制的两种消费姿势

          本文链接:https://www.haomeiwen.com/subject/dixzsltx.html