使用spring自带的发布订阅机制来实现消息发布订阅

背景

公司的项目以前代码里面有存在使用spring自带发布订阅的代码,因此稍微学习一下如何使用,并了解一下这种实现方式的优缺点。

优点

  • 实现方便,代码方面基本只需要定义消息体和消费者,适用于小型应用程序。
  • 不依赖外部中间件,因而不需要复杂的配置、部署。

缺点

  • 无法提供消息持久性,项目一旦重启,消息就会丢失,因而不适合实现延迟队列。
  • 对比消息队列,无法实现复杂的消息过滤、路由过滤。
  • 无法实现跨应用程序的事件通信。不同应用程序之间的事件发布和订阅更为容易。

发布订阅模式的优缺点我就不说了,就说说不同实现方式之间的优缺点。

一、创建消息类

消息类需要继承ApplicationEvent类。因为java调用构造函数的机制就是默认会调用父类的构造函数,而ApplicationEvent类只有一个单参数的构造函数,无法自动调用,每个构造函数都需要显式调用父类的构造函数。也就是super(source);

package org.jeecg.modules.test.testPublic;
import org.springframework.context.ApplicationEvent;
import java.util.Objects;

/**
 * @ClassName: MyEvent
 * @Author: zjc
 * @Date: 2023/8/30 18:22
 * @Description:
 **/
public class MyEvent extends ApplicationEvent {
	private String taskId;
	private Integer sourceType;
	
	public MyEvent(Object source) {
		super(source);
	}
	/**
	 *
	 *  @param source   触发事件的对象,可随便传,不过建议传自己可能用得到的对象。好像在调用放直接this的挺多
	 * @param taskId    任务id,自己定义的事件要处理的内容
	 * @param sourceType    自己定义的源类型,用来在多场景触发情况下区分不同场景的标志
	 * @return: null
	 **/
	public MyEvent(Object source,String taskId,Integer sourceType) {
		super(source);
		this.taskId=taskId;
		this.sourceType=sourceType;
	}

	public String getTaskId() {
		return taskId;
	}

	public void setTaskId(String taskId) {
		this.taskId = taskId;
	}

	public Integer getSourceType() {
		return sourceType;
	}

	public void setSourceType(Integer sourceType) {
		this.sourceType = sourceType;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		MyEvent myEvent = (MyEvent) o;
		return Objects.equals(taskId, myEvent.taskId) && Objects.equals(sourceType, myEvent.sourceType);
	}

	@Override
	public int hashCode() {
		return Objects.hash(taskId, sourceType);
	}

	@Override
	public String toString() {
		return "MyEvent{" +
				"taskId='" + taskId + '\'' +
				", sourceType=" + sourceType +
				'}';
	}
}

二、发布消息

发布消息可以直接使用ApplicationContext对象调用publishEvent方法。因为ApplicationContext接口继承了ApplicationEventPublisher接口。注意消息类必须要继承ApplicationEvent类才能作为参数发布消息。

package org.jeecg.modules.test.testPublic;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName: EventController
 * @Author: 
 * @Date: 2023/8/31 17:53
 * @Description:
 **/
@RestController
@Api("test")
@RequestMapping("/test")
public class EventController {
	@Autowired
	private ApplicationContext applicationContext;
	@ApiOperation("testEvent")
	@GetMapping("/testEvent")
	public void testEvent(){
		MyEvent myEvent=new MyEvent(this,"123456",1);
		applicationContext.publishEvent(myEvent);
	}
	@ApiOperation("testEvent1")
	@GetMapping("/testEvent1")
	public void testEvent1(){
		MyEvent myEvent=new MyEvent(this,"123456",2);
		applicationContext.publishEvent(myEvent);
	}
}

三、监听消息

监听消息类需要实现ApplicationListener接口,并通过泛型传入要监听的消息类,并重写onApplicationEvent方法。spring内的同一个消息可以有多个监听类,一旦监听到消息,监听该消息的全部监听类都会执行。

监听类1:

package org.jeecg.modules.test.testPublic;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @ClassName: MyEventListener
 * @Author: 
 * @Date: 2023/8/31 17:50
 * @Description:
 **/
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {
		System.out.println("消费者开始消费"+event.toString()+event.getSource().toString());
	}
}

消费者2

package org.jeecg.modules.test.testPublic;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @ClassName: MyEventListener
 * @Author: 
 * @Date: 2023/8/31 17:50
 * @Description:
 **/
@Component
public class MyEventListener1 implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {
		System.out.println("消费者1开始消费"+event.toString()+event.getSource().toString());
	}
}

四、测试

调用testEvent接口

在这里插入图片描述

调用testEvent1接口

在这里插入图片描述

结果均符合预期,可以通过在消息体里面加一个字段来区分消息来着不同的触发场景。
即使没有在结构体加上区分消息来源的标识,也可以用消息一开始传入的源对象来大概定位到是哪一个类里面触发的消息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 `ConcurrentLinkedQueue` 实现发布订阅模式,你可以创建一个 `EventBus` 类,其中包含一个 `ConcurrentLinkedQueue` 作为事件队列,并提供相应的方法来发布事件和订阅事件。以下是一个简单的示例: ```java import java.util.concurrent.ConcurrentLinkedQueue; public class EventBus { private ConcurrentLinkedQueue<EventListener> listeners; public EventBus() { listeners = new ConcurrentLinkedQueue<>(); } public void subscribe(EventListener listener) { listeners.add(listener); } public void unsubscribe(EventListener listener) { listeners.remove(listener); } public void publishEvent(Object event) { for (EventListener listener : listeners) { listener.onEvent(event); } } } ``` 然后,你可以创建一个 `EventListener` 接口或抽象类,让其他类实现该接口或继承该类,并在需要监听事件的地方注册为订阅者。以下是一个示例: ```java public interface EventListener { void onEvent(Object event); } ``` 现在,你可以在需要发布事件的地方创建一个 `EventBus` 实例,并使用其 `publishEvent` 方法来发布事件。订阅者可以实现 `EventListener` 接口,并通过调用 `subscribe` 方法注册为订阅者。当事件被发布时,订阅者的 `onEvent` 方法会被调用。 以下是一个简单的示例,展示了如何使用 `EventBus` 实现发布订阅模式: ```java public class MyApp { public static void main(String[] args) { EventBus eventBus = new EventBus(); // 创建订阅者 EventListener listener1 = new EventListener() { @Override public void onEvent(Object event) { System.out.println("Listener 1: Event received: " + event.toString()); } }; EventListener listener2 = new EventListener() { @Override public void onEvent(Object event) { System.out.println("Listener 2: Event received: " + event.toString()); } }; // 注册订阅者 eventBus.subscribe(listener1); eventBus.subscribe(listener2); // 发布事件 eventBus.publishEvent("Hello, world!"); } } ``` 当运行 `MyApp` 类时,会输出以下内容: ``` Listener 1: Event received: Hello, world! Listener 2: Event received: Hello, world! ``` 这就是使用 `ConcurrentLinkedQueue` 实现发布订阅模式的简单示例。你可以根据具体需求进行扩展和定制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值