springboot publish event 事件机制demo分享

作者:阿拉的梦想 时间:2022-11-19 23:45:45 

1. 使用ApplicationEventPublisher 发布事件

复制下面全部代码,右键包名,粘贴即可生成java类,执行即可看到效果。

事件机制

  • 需要自定义一个事件类继承ApplicationEvent;

  • 需要自定义一个 * 类实现ApplicationListener接口,或普通类的方法中使用@EventListener注解;

  • 使用默认发布器ApplicationEventPublisher发布即可;

  • 事件类不需要注入到IOC; * 需要注入到IOC;ApplicationEventPublisher用Autowired注入进来即可;

  • 默认情况下,事件发布与执行是同步的,事件执行完毕,发布者才会执行下面的逻辑;

package com.example.controller;

import com.alibaba.fastjson.JSON;
import com.example.SpringbootRedisApplication;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Objects;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringbootRedisApplication.class)
@Slf4j
@EnableAsync
public class PublishEventDemo {
   /**
    * 事件机制:
    * 需要自定义一个事件类继承ApplicationEvent;
    * 需要自定义一个 * 类实现ApplicationListener接口,或普通类的方法中使用@EventListener注解;
    * 使用默认发布器ApplicationEventPublisher发布即可;
    * 事件类不需要注入到IOC; * 需要注入到IOC;ApplicationEventPublisher用Autowired注入进来即可;
    */

/**
    * 事件发布器
    */
   @Autowired
   private ApplicationEventPublisher eventPublisher;

/**
    * 测试类
    */
   @Test
   public void publishTest() throws InterruptedException {
       Task task = new Task();
       task.setId(1L);
       task.setTaskName("测试任务");
       task.setTaskContext("任务内容");
       task.setFinish(false);
       MyEvent event = new MyEvent(task);
       log.info("开始发布任务");
       eventPublisher.publishEvent(event);
       //applicationContext.publishEvent(event);
       log.info("结束发布任务");
       Thread.sleep(10000);
   }
}

/**
* 任务类
* 任务就是一个普通的类,用来保存你要发布事件的内容。这个没有特殊限制,可以根据自己业务随意设置。
*/
@Data
class Task {

private Long id;

private String taskName;

private String taskContext;

private boolean finish;
}

/**
* 事件类
* 事件类需要继承org.springframework.context.ApplicationEvent,这样发布的事件才能被Spring所识别
*/
@Slf4j
class MyEvent<T> extends ApplicationEvent {

private Task task;

public MyEvent(Task task) {
       super(task);
       this.task = task;
   }

public Task getTask() {
       return task;
   }
}

/**
* 事件监听类
* 事件的 * 需要实现org.springframework.context.ApplicationListener,并且需要注入到容器之中。
*/
@Component
@Slf4j
class MyEventListenerA implements ApplicationListener<MyEvent> {
   /**
    * 监听方式1:实现ApplicationListener接口,重写onApplicationEvent方法
    * 需要使用Component注入IOC
    */
   @SneakyThrows
   @Async
   @Override
   public void onApplicationEvent(MyEvent MyEvent) {
       Thread.sleep(5000);
       if (Objects.isNull(MyEvent)) {
           return;
       }
       Task task = MyEvent.getTask();
       log.info(" * A接收任务:{}", JSON.toJSONString(task));
       task.setFinish(true);
       log.info(" * A此时完成任务");

}
}

@Component
@Slf4j
class MyEventListenerB implements ApplicationListener<MyEvent> {
   /**
    * 监听方式2:接口和注解混合使用
    * 但此时 @EventListener不能与注解@Async在同一个类中使用,会报错,至于为什么,不知道;
    * 需要使用Component注入IOC
    */
   //@Async//加上这个,@EventListener的方法就会报java.lang.IllegalStateException: Failed to load ApplicationContext
   @SneakyThrows
   @Override
   public void onApplicationEvent(MyEvent MyEvent) {
       Thread.sleep(1000);
       if (Objects.isNull(MyEvent)) {
           return;
       }
       Task task = MyEvent.getTask();
       log.info(" * B接收任务:{}", JSON.toJSONString(task));
       task.setFinish(true);
       log.info(" * B此时完成任务");
   }

@EventListener
   public void someMethod(MyEvent event) throws InterruptedException {
       Thread.sleep(1000);
       log.info(" * @EventListenerB收到={}", event.getTask());
   }
}

@Component
@Slf4j
class MyEventListennerC {
   /**
    * 监听方式3:注解@EventListener的 * 不需要实现任何接口
    * 需要使用Component注入IOC
    */
   @EventListener
   public void someMethod(MyEvent event) throws InterruptedException {
       Thread.sleep(1000);
       log.info(" * @EventListenerC收到={}", event.getTask());
   }
}

运行日志:

2020-11-30 18:13:56.238  INFO 19776 --- [           main] com.example.controller.PublishEventDemo  : Started PublishEventDemo in 11.098 seconds (JVM running for 13.737)
2020-11-30 18:13:56.902  INFO 19776 --- [           main] com.example.controller.PublishEventDemo  : 开始发布任务
2020-11-30 18:13:57.904  INFO 19776 --- [           main] com.example.controller.MyEventListenerB  : * @EventListenerB收到=Task(id=1, taskName=测试任务, taskContext=任务内容, finish=false)
2020-11-30 18:13:58.905  INFO 19776 --- [           main] c.example.controller.MyEventListennerC   : * @EventListenerC收到=Task(id=1, taskName=测试任务, taskContext=任务内容, finish=false)
2020-11-30 18:13:59.920  INFO 19776 --- [           main] com.example.controller.MyEventListenerB  : * B接收任务:{"finish":false,"id":1,"taskContext":"任务内容","taskName":"测试任务"}
2020-11-30 18:13:59.921  INFO 19776 --- [           main] com.example.controller.MyEventListenerB  : * B此时完成任务
2020-11-30 18:13:59.921  INFO 19776 --- [           main] com.example.controller.PublishEventDemo  : 结束发布任务
2020-11-30 18:14:03.913  INFO 19776 --- [         task-1] com.example.controller.MyEventListenerA  : * A接收任务:{"finish":true,"id":1,"taskContext":"任务内容","taskName":"测试任务"}
2020-11-30 18:14:03.913  INFO 19776 --- [         task-1] com.example.controller.MyEventListenerA  : * A此时完成任务
2020-11-30 18:14:09.958  INFO 19776 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

事件发送后,会等待事件执行完毕,因此他们是同步的。若想异步执行事件,可以把@Async加到监听方法上;

2. 使用ApplicationContext发布事件

与上例不同之处

  • 使用ApplicationContext发布事件,ApplicationContext实现了ApplicationEventPublisher接口;

  • 使用ApplicationContextEvent 定义事件,ApplicationContextEvent 继承了ApplicationEvent类;

demo代码:

package com.example.controller;

import ch.qos.logback.classic.Logger;
import com.example.SpringbootRedisApplication;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RestController;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringbootRedisApplication.class)
@RestController
@Slf4j
public class PublishEventDemo2 {
   /**
    * 使用ApplicationContext发布事件
    */
   @Autowired
   ApplicationContext applicationContext;

@Test
   public void send() {
       MyEvent2 myEvent2 = new MyEvent2(applicationContext);
       myEvent2.setData("数据");
       log.info("开始发送事件");
       applicationContext.publishEvent(myEvent2);
       log.info("结束发送事件");
   }
}

/**
* * 类
*/
@Slf4j
@Component
class MyEventListener2 implements ApplicationListener<MyEvent2> {
   @Override
   public void onApplicationEvent(MyEvent2 event) {
       log.info(" * MyEventListener2收到={}", event);
   }

@EventListener
   public void someMethod(MyEvent2 event) {
       log.info(" * MyEventListener2@EventListener收到={}", event);
   }
}

/**
* * 类
*/
@Slf4j
@Component
class MyEventListenner1 {
   @EventListener
   public void someMethod(MyEvent2 event) {
       log.info(" * MyEventListenner1收到={}", event);
   }
}

/**
* 事件类
*
* @param <T>
*/
@ToString
class MyEvent2<T> extends ApplicationContextEvent {
   private T data;

public MyEvent2(ApplicationContext source) {
       super(source);
   }

public T getData() {
       return data;
   }

public void setData(T data) {
       this.data = data;
   }
}

运行日志:

2020-11-30 18:03:38.638  INFO 15792 --- [           main] c.example.controller.PublishEventDemo2   : Started PublishEventDemo2 in 9.571 seconds (JVM running for 12.677)
2020-11-30 18:03:39.355  INFO 15792 --- [           main] c.example.controller.PublishEventDemo2   : 开始发送事件
2020-11-30 18:03:39.358  INFO 15792 --- [           main] com.example.controller.MyEventListener2  : * MyEventListener2@EventListener收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 --- [           main] c.example.controller.MyEventListenner1   : * MyEventListenner1收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 --- [           main] com.example.controller.MyEventListener2  : * MyEventListener2收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 --- [           main] c.example.controller.PublishEventDemo2   : 结束发送事件
2020-11-30 18:03:39.435  INFO 15792 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

事件发送后,会等待事件执行完毕,因此他们是同步的。若想异步执行事件,可以把@Async加到监听方法上;

来源:https://blog.csdn.net/A434534658/article/details/110392200

标签:springboot,publish,event,事件机制
0
投稿

猜你喜欢

  • Python基于pyjnius库实现访问java类

    2022-01-03 12:29:06
  • 浅谈java+内存分配及变量存储位置的区别

    2022-07-09 00:46:47
  • C# Split函数根据特定分隔符分割字符串的操作

    2023-11-07 07:40:25
  • Spring bean为什么需要依赖注入

    2022-01-24 11:07:21
  • Java编程实现高斯模糊和图像的空间卷积详解

    2022-06-18 10:44:34
  • SpringBoot集成Beetl后统一处理页面异常的方法

    2023-11-10 19:57:55
  • Servlet3.0实现文件上传的方法

    2023-08-15 00:52:44
  • Flutter路由传递参数及解析实现

    2023-06-22 11:48:45
  • Java Swing 多线程加载图片(保证顺序一致)

    2023-10-26 10:10:36
  • 如何安装java的运行环境IDEA

    2022-09-20 10:42:38
  • Java集合教程之Collection实例详解

    2022-12-01 23:45:13
  • Java 运算符详情

    2022-02-12 05:54:43
  • Java基于servlet监听器实现在线人数监控功能的方法

    2021-08-19 11:38:24
  • C#创建WebService接口并连接的全过程

    2023-04-07 07:47:21
  • SpringMVC框架整合Junit进行单元测试(案例详解)

    2022-04-10 20:53:37
  • Spring Cloud Gateway网关XSS过滤方式

    2021-08-07 13:16:53
  • 关于spring boot中几种注入方法的一些个人看法

    2022-09-07 10:15:14
  • 简介Winform中创建用户控件

    2021-10-14 04:39:37
  • c#基础知识---委托,匿名函数,lambda

    2023-06-12 18:18:07
  • 解决springcloud中Feign导入依赖为unknow的情况

    2022-02-03 14:05:45
  • asp之家 软件编程 m.aspxhome.com