什么是观察者模式

在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。x

应用场景

Zookeeper事件通知节点、消息订阅通知、安卓开发事件注册

分布式配置中心

原理类图

img

抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。

具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

手写简单实现

具体代码

定义事件对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @Author shuzhuo
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EvenObject {

private String id;

private String message;


}

定义事件监听

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @Author shuzhuo
*/
public interface EventListener {


/**
* 事件处理
* @param evenObject 事件对象
*/
void doEvent(EvenObject evenObject);
}

事件管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @author shuzhuo
*/
public class EventManager {

/**
* 存放所有的事件对象
*/
Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();


public EventManager(Enum<EventType>... operations) {
for (Enum<EventType> operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}

public enum EventType {
MQ, Message
}

/**
* 订阅
*
* @param eventType 事件类型
* @param listener 监听
*/
public void subscribe(Enum<EventType> eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}

/**
* 取消订阅
*
* @param eventType 事件类型
* @param listener 监听
*/
public void unsubscribe(Enum<EventType> eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}

/**
* 通知
*
* @param eventType 事件类型
* @param result 结果
*/
public void notify(Enum<EventType> eventType, EvenObject result) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.doEvent(result);
}
}
}

业务抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @Author shuzhuo
*/
public abstract class BusinessService {

private EventManager eventManager;

public BusinessService() {
eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
eventManager.subscribe(EventManager.EventType.MQ, new MqEventListener());
eventManager.subscribe(EventManager.EventType.Message, new MessageEventListener());
}

public EvenObject draw(String id) {
EvenObject lotteryResult = doDraw(id);
// 需要什么通知就给调用什么方法
eventManager.notify(EventManager.EventType.MQ, lotteryResult);
eventManager.notify(EventManager.EventType.Message, lotteryResult);
return lotteryResult;
}

protected abstract EvenObject doDraw(String id);
}

业务实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.time.LocalDateTime;

/**
* @Author shuzhuo
*/
public class BusinessServiceImpl extends BusinessService{

@Override
protected EvenObject doDraw(String id) {
EvenObject evenObject = new EvenObject(id,"message"+ LocalDateTime.now());
return evenObject;

}
}

测试

1
2
3
4
5
6
7

public static void main(String[] args) {
BusinessService businessService = new BusinessServiceImpl();
EvenObject draw = businessService.draw("123");
log.info("测试结果:{},{}", draw.getId(),draw.getMessage());
}

1
2
3
4
5
6
21:44:54.838 [main] INFO com.example.springbooot.observer.MqEventListener - 给用户 123 发送MQ通知(短信):message2021-09-02T21:44:54.836
21:44:54.842 [main] INFO com.example.springbooot.observer.MessageEventListener - 给用户 123 发送短信通知(短信):message2021-09-02T21:44:54.836
21:44:54.843 [main] INFO com.example.springbooot.observer.Main - 测试结果:123,message2021-09-02T21:44:54.836

Process finished with exit code 0

JDK自带观察实现

自定义主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Observable;

/**
* @author shuzhuo
*/
public class MessageObservable extends Observable {

@Override
public void notifyObservers(Object arg) {
// 1.改变数据
setChanged();
// 2.通知所有的观察者改变
super.notifyObservers(arg);
}
}

自定义观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.extern.slf4j.Slf4j;

import java.util.Observable;
import java.util.Observer;

/**
* @author shuzhuo
*/
@Slf4j
public class EmailObServer implements Observer {


@Override
public void update(Observable o, Object arg) {
// 1.获取主题
MessageObservable messageObServable = (MessageObservable) o;
log.info("发送邮件内容:{}",arg);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lombok.extern.slf4j.Slf4j;

import java.util.Observable;
import java.util.Observer;

/**
* @author shuzhuo
*/
@Slf4j
public class SmsObServer implements Observer {


@Override
public void update(Observable o, Object arg) {
log.info("发送sms:{}",arg);
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.LocalDateTime;

/**
* @author shuzhuo
*/
public class JdkObServer {

public static void main(String[] args) {
//1.创建主题
MessageObservable messageObServable = new MessageObservable();
// 2.添加订阅者
messageObServable.addObserver(new EmailObServer());
messageObServable.addObserver(new SmsObServer());
// 3.组装消息内容
messageObServable.notifyObservers(LocalDateTime.now());
}
}

Spring封装实现

创建Event事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;

/**
* @Author shuzhuo
*/
public class MyApplicationEvent extends ApplicationEvent {

@Getter
@Setter
private String msg;

public MyApplicationEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
}

创建Event事件监听

实现 接口方式

实现 ApplicationListener 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
* @author shuzhuo
*/
@Component
public class EmailListener implements ApplicationListener<MyApplicationEvent> {

@Override
@Async
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println(Thread.currentThread().getName() + "发送邮件内容:" + event.getMsg());
}
}

实现 SmartApplicationListener 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
* @Author shuzhuo
* @Date 2021/9/2 22:41
*/
@Component
public class MqListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
// 判断逻辑,只有返回true ,才执行事件监听
System.out.println(eventType == MyApplicationEvent.class);
return eventType == MyApplicationEvent.class;
}

/**
* return 的数值越小证明优先级越高,执行顺序越靠前
*
* @return
*/
@Override
public int getOrder() {
return 10;
}

@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(Thread.currentThread().getName() + "发送mq内容:" + ((MyApplicationEvent) event).getMsg());
}
}

注解方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
* 注解方式比较简单,并不需要实现任何接口
*
* @author shuzhuo
*/
@Component
public class SmsListener {

/**
* 注册监听实现方法
*/
@EventListener
public void sendEmail(MyApplicationEvent event) {
System.out.println(Thread.currentThread().getName() + "发送短信内容:" + event.getMsg());
}
}

事件发布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

/**
* @Author shuzhuo
* @Date 2021/9/2 22:15
*/
@RestController
public class PushController {

@Autowired
private ApplicationContext applicationContext;

@RequestMapping("/push")
public void push(){
//发布事件
MyApplicationEvent myApplicationEvent = new MyApplicationEvent(this,LocalDateTime.now().toString());
applicationContext.publishEvent(myApplicationEvent);
}

}

测试

1
2
3
http-nio-8089-exec-1发送短信内容:2021-09-02T22:45:51.712
http-nio-8089-exec-1发送mq内容:2021-09-02T22:45:51.712
http-nio-8089-exec-1发送邮件内容:2021-09-02T22:45:51.712