今天来聊聊Spring中的观察者模式
前言
今天来看一看什么是观察者模式,在现实生活中,观察者模式处处可见,就以微信公众号来说吧,每当一个用户订阅了一个公众号,那么就会收到公众号发来的消息,这里公众号就是被观察的对象,用户就是观察者。而在设计模式中,被观察者被称之为主题。
一、观察者设计模式中涉及到的角色
在观察者设计模式中,一般有四个角色:
-
抽象主题角色(Subject)
-
具体主题角色(ConcreteSubject)
-
抽象观察者角色(Observer)
-
具体观察者角色(ConcreteObserver)
其中,主题需要有一个列表字段,用来保存观察者的引用,提供两个方法(虚方法),即删除观察者以及增加观察者,还需要提供一个给客户端调用的方法,通知各个观察者。
二、使用设计模式的优缺点
优点
- 主题和观察者通过抽象,建立了一个松耦合的关系,主题只知道当前有哪些观察者,并且发送通知,但是不知道观察者具体会执行怎样的动作。
- 符合开闭原则,如果需要新增一个观察者,只需要写一个类去实现抽象观察者角色即可,不需要改动原来的代码。
缺点
- 客户端必须知道所有的观察者,并且进行增加观察者和删除观察者的操作。
- 如果有很多观察者,那么所有的观察者收到通知,可能需要花费很久时间。
三、示例代码
我们可以用代码举一个简单例子,就以发布新闻为例。
// 此类不属于观察者模式必须的类,用来存放事件的信息。
public class News {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public interface Subject {
List<People> peopleList = new ArrayList<>();
default void add(People people) {
peopleList.add(people);
}
default void remove(People people) {
peopleList.remove(people);
}
void update();
}
抽象主题角色,在这个角色中,有一个字段peopleList,用来保存观察者的引用,同时定义了两个接口,这两个接口是给客户端调用的,用来删除观察者以及增加观察者,还提供一个方法,此方法需要被具体主题角色重写,用来通知各个观察者。
// 具体主题角色,重写了抽象主题角色的方法,
// 循环列表,通知各个观察者。
public class NewsSubject implements Subject{
public void update() {
for (People people : peopleList) {
News news = new News();
news.setContent("太阳获得了总冠军");
news.setTitle("保罗即将获得首冠");
people.update(news);
}
}
}
// 抽象观察者角色,定义了一个接口,具体观察者角色需要重写这个方法。
public interface People {
void update(News news);
}
下面就可以定义具体的观察者了。
public class PeopleA implements People {
@Override
public void update(News news) {
System.out.println("这个新闻真好");
}
}
public class PeopleB implements People {
@Override
public void update(News news) {
System.out.println("这个新闻一般般");
}
}
public class PeopleC implements People {
@Override
public void update(News news) {
System.out.println("这个新闻真无聊");
}
}
public class Main {
public static void main(String[] args) {
Subject subject = new NewsSubject();
subject.add(new PeopleA());
subject.add(new PeopleB());
subject.add(new PeopleC());
subject.update();
}
}
四、Spring中的观察者设计模式
那么,在Spring中观察者是如何体现的呢?Spring中的事件编程模型就是观察者模式的实现。在Spring中定义了一个ApplicationListener接口,用来监听Application的事件,Application其实就是ApplicationContext,ApplicationContext内置了几个事件,其中比较容易理解的是:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent,从名称上来看,就知道这几个事件是什么时候被触发的了。接下来我们说一下如何利用Spring中的事件编程模型来定义自定义事件,并且发布事件。
首先,我们需要定义一个事件,来实现ApplicationEvent接口,代表这是一个Application事件。
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
还需要定义一个监听器,当然,在这里需要监听MyEvent事件。
@Component
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("我订阅的事件已经到达");
}
}
现在有了事件,也有了监听器,是不是还少了发布者,不然谁去发布事件呢?
@Component
public class MyEventPublish implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(Object obj) {
this.publisher.publishEvent(obj);
}
}
发布者,需要实现ApplicationEventPublisherAware 接口,重写publish方法,方法的参数obj就是用来存放发布事件数据。setApplicationEventPublisher是Spring内部主动调用的,可以简单的理解为初始化发布者。现在就剩最后一个角色了,监听器有了,发布者有了,事件也有了,还少一个触发者。
@Component
public class Service {
@Autowired
private MyEventPublish publish;
public void publish() {
publish.publish(new MyEvent(this));
}
}
其中publish方法就是给客户端调用的,用来触发事件,可以很清楚的看到传入了new MyEvent(this),这样发布者就可以知道我要触发什么事件和是谁触发了事件。
欢迎大家添加个人公众号,一起进步努力。