发布订阅模式笔记
定义
发布—订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
使用
- 编码实现
package observer;
public interface Subject {
//添加观察者
void addObserver(Observer obj);
//移除观察者
void deleteObserver(Observer obj);
//当主题方法改变时,这个方法被调用,通知所有的观察者
void notifyObserver();
}
package observer;
public interface Observer {
//当主题状态改变时,更新通知
public void update(int version);
}
package observer;
import java.util.ArrayList;
import java.util.List;
public class MagazineSubject implements Subject{
//存放订阅者
private List<Observer> observers=new ArrayList<Observer>();
//期刊版本
private int version;
@Override
public void addObserver(Observer obj) {
observers.add(obj);
}
@Override
public void deleteObserver(Observer obj) {
int i = observers.indexOf(obj);
if(i>=0){
observers.remove(obj);
}
}
@Override
public void notifyObserver() {
for(int i=0;i<observers.size();i++){
Observer o=(Observer)observers.get(i);
o.update(version);
}
}
//该杂志发行了新版本
public void publish(){
//新版本
this.version++;
//信息更新完毕,通知所有观察者
notifyObserver();
}
}
package observer;
public class CustomerObserver implements Observer{
//订阅者名字
private String name;
private int version;
public CustomerObserver(String name){
this.name = name;
}
@Override
public void update(int version) {
this.version=version;
System.out.println("该杂志出新版本了");
this.buy();
}
public void buy(){
System.out.println(name+"购买了第"+version+"期的杂志!");
}
}
package observer;
public class Main{
public static void main(String[] args) {
//创建主题(被观察者)
MagazineSubject magazine = new MagazineSubject();
//创建三个不同的观察者
CustomerObserver a = new CustomerObserver("A");
CustomerObserver b = new CustomerObserver("B");
CustomerObserver c = new CustomerObserver("C");
//将观察者注册到主题中
magazine.addObserver(a);
magazine.addObserver(b);
magazine.addObserver(c);
//更新主题的数据,当数据更新后,会自动通知所有已注册的观察者
magazine.publish();
}
}
- Redis的发布订阅模式
@Component
public interface RedisMsg {
/**
* Redis订阅者接受消息的接口
*
* @param message 订阅的消息
*/
void receiveMessage(String message);
}
public class RedisChannelSub implements RedisMsg {
@Override
public void receiveMessage(String message) {
//注意通道调用的方法名要和 RedisPubSubConfig 的listenerAdapter的 MessageListenerAdapter 参数2相同
System.out.println("这是RedisChannelSub" + "-----" + message);
}
}
public class RedisPmpSub implements RedisMsg {
/**
* 接收消息的方法
*
* @param message 订阅消息
*/
@Override
public void receiveMessage(String message) {
//注意通道调用的方法名要和RedisConfig2的listenerAdapter的MessageListenerAdapter参数2相同
System.out.println("这是RedisPmpSub---" + message);
}
}
@Configuration
public class RedisPubSubConfig {
/**
* Redis消息监听器容器
*
* @param connectionFactory /
* @return /
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅了一个叫pmp和channel 的通道,多通道
container.addMessageListener(listenerAdapter(new RedisPmpSub()), new PatternTopic("pmp"));
container.addMessageListener(listenerAdapter(new RedisChannelSub()), new PatternTopic("channel"));
//这个container 可以添加多个 messageListener
return container;
}
/**
* 配置消息接收处理类
*
* @param redisMsg 自定义消息接收类
* @return Redis的监听适配器
*/
@Bean
@Scope("prototype")
MessageListenerAdapter listenerAdapter(RedisMsg redisMsg) {
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
//也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
//注意2个通道调用的方法都要为receiveMessage
return new MessageListenerAdapter(redisMsg, "receiveMessage");
}
}
@EnableScheduling
@Component
public class TestScheduleRedisPublishController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 向redis消息队列index通道发布消息
*/
@Scheduled(fixedRate = 2000)
public void sendMessage() {
stringRedisTemplate.convertAndSend("pmp", String.valueOf(Math.random()));
stringRedisTemplate.convertAndSend("channel", String.valueOf(Math.random()));
}
}
- 消息中间件
优缺点
优点:
广泛应用于异步编程,它可以代替我们传统的回调函数,我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点
取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起 。虽然不知道彼此的细节,但不影响相互通信。更重要的是,其中一个对象改变不会影响另一个对象
缺点:
创建订阅者需要消耗一定的时间和内存
虽然可以弱化对象之间的联系,但是如果过度使用的话,代码反而不太好理解和维护。特别是有多个发布者和订阅者的嵌套到一起的时候,要跟踪一个bug并不是一件轻松的事情