观察者模式(英文 Observer Pattern),也叫监听模式,发布-订阅模式,属于行为型模式。当一个对象的状态或者数据更新时,依赖于该对象的其他对象也需要做出相应的更新,该模式经常应用于实现订阅功能,例如进行一次消费后,订单系统需要进行处理,需要发送短信提示,需要发送邮件提示,需要日志记录,等等,此外,Spring的事件机制也是观察者模式的应用。
观察者模式与桥接模式有点类似,桥接模式主要是针对多对多的场景,而观察者模式主要是针对一对多的场景;此外,观察者模式涉及集合的维护,而桥接模式仅仅是接口的引用。
// 被观察者接口
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyAllObserver();
}
// 被观察者实现类
import java.util.ArrayList;
import java.util.List;
public class ConcreteSubject implements Subject {
private List<Observer> list = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
list.add(observer);
}
@Override
public void removeObserver(Observer observer) {
list.remove(observer);
}
@Override
public void notifyAllObserver() {
list.forEach(Observer::update);
}
}
// 观察者接口
public interface Observer {
void update();
}
// 4、观察者实现类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConcreteObserver implements Observer {
private static final Logger LOGGER = LoggerFactory.getLogger(ConcreteObserver.class);
private Subject subject;
// 将观察者注册进被观察者里面
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.addObserver(this);
}
@Override
public void update() {
LOGGER.info("concreteObserver update.");
}
}
// 5、模拟调用
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class TestObserverPattern implements ApplicationRunner {
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
ConcreteSubject concreteSubject = new ConcreteSubject();
new ConcreteObserver(concreteSubject);
concreteSubject.notifyAllObserver();
}
}
// 6、结果展示
2022-08-07 16:43:28.961 INFO 9296 --- [ main] c.test.observerPattern.ConcreteObserver : concreteObserver update.
// 1、自定义事件类
import org.springframework.context.ApplicationEvent;
import java.util.Map;
public class OrderEvent extends ApplicationEvent {
private String name;
private Map<String,String> conf;
public OrderEvent(Object source, String name, Map<String, String> conf) {
super(source);
this.name = name;
this.conf = conf;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getConf() {
return conf;
}
public void setConf(Map<String, String> conf) {
this.conf = conf;
}
}
// 2、被监听者实现类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
public class OrderPublisher implements ApplicationRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderPublisher.class);
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
LOGGER.info("publish order event begin.");
OrderEvent orderEvent = new OrderEvent(this, "zhangsan", new HashMap<>());
// 也可以通过 applicationContext.publishEvent(orderEvent) 来发布事件
applicationEventPublisher.publishEvent(orderEvent);
LOGGER.info("publish order event end.");
}
}
// 3、监听者实现类(可以多个)
// 实现 ApplicationListener 接口,传入特定类型的事件,或者使用@EventListener注解
import com.test.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class LogListener implements ApplicationListener<OrderEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(LogListener.class);
@Override
public void onApplicationEvent(OrderEvent orderEvent) {
LOGGER.info("log listener begin with [{}].", orderEvent.getName());
TimeUtils.sleep(10000, LOGGER);
}
}
import com.test.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
// @Async 开启异步执行,需要在启动类加上@EnableAsync注解
public class EmailListener implements ApplicationListener<OrderEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(EmailListener.class);
@Override
public void onApplicationEvent(OrderEvent orderEvent) {
LOGGER.info("email listener begin with [{}].", orderEvent.getName());
TimeUtils.sleep(20000, LOGGER);
}
}
import com.test.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
@Configuration
public class OrderEventConfig {
public static final Logger LOGGER = LoggerFactory.getLogger(OrderEventConfig.class);
@EventListener(classes = {
OrderEvent.class
})
// 使用 @EventListener 注解,则可以在一个类里面监听多种事件
public void orderEventHandler(OrderEvent orderEvent) {
LOGGER.info("order listener begin with [{}].", orderEvent.getName());
TimeUtils.sleep(10000, LOGGER);
}
@EventListener
public void allEventHandler(ApplicationEvent applicationEvent) {
LOGGER.error("all event listener begin , event class is [{}].", applicationEvent.getClass().getName());
}
}
// 4、时间工具类
import org.slf4j.Logger;
public class TimeUtils {
public static void sleep(int ms, Logger logger) {
try {
logger.info("thread sleep [{}] ms begin.", ms);
Thread.sleep(ms);
logger.info("thread sleep [{}] ms end.", ms);
} catch (InterruptedException e) {
logger.error("thread sleep error.", e);
}
}
}
// 5、结果展示
2022-08-07 17:49:54.323 INFO 17576 --- [ main] c.t.o.springEvent.OrderPublisher : publish order event begin.
2022-08-07 17:49:54.324 INFO 17576 --- [ main] c.t.o.springEvent.EmailListener : email listener begin with [zhangsan].
2022-08-07 17:49:54.328 INFO 17576 --- [ main] c.t.o.springEvent.EmailListener : thread sleep [20000] ms begin.
2022-08-07 17:50:14.336 INFO 17576 --- [ main] c.t.o.springEvent.EmailListener : thread sleep [20000] ms end.
2022-08-07 17:50:14.336 INFO 17576 --- [ main] c.t.o.springEvent.LogListener : log listener begin with [zhangsan].
2022-08-07 17:50:14.336 INFO 17576 --- [ main] c.t.o.springEvent.LogListener : thread sleep [10000] ms begin.
2022-08-07 17:50:24.350 INFO 17576 --- [ main] c.t.o.springEvent.LogListener : thread sleep [10000] ms end.
2022-08-07 17:50:24.350 INFO 17576 --- [ main] c.t.o.springEvent.OrderPublisher : publish order event end.
2022-08-07 17:50:24.356 INFO 17576 --- [ main] com.test.SpringProjectApplication : Started SpringProjectApplication in 37.643 seconds (JVM running for 38.592)
如上所示是串行执行,如果要开启异步,可以通过在启动类加上@EnableAsync注解,在需要异步执行的监听类加上@Async,结果如下,异步处理的会通过线程处理,不影响主进程应用的启动:
2022-08-07 17:57:51.265 INFO 17292 --- [ main] c.t.o.springEvent.OrderPublisher : publish order event begin.
2022-08-07 17:57:51.276 INFO 17292 --- [ main] .s.a.AnnotationAsyncExecutionInterceptor : No TaskExecutor bean found for async processing
2022-08-07 17:57:51.277 INFO 17292 --- [ main] c.t.o.springEvent.LogListener : log listener begin with [zhangsan].
2022-08-07 17:57:51.277 INFO 17292 --- [cTaskExecutor-1] c.t.o.springEvent.EmailListener : email listener begin with [zhangsan].
2022-08-07 17:57:51.281 INFO 17292 --- [ main] c.t.o.springEvent.LogListener : thread sleep [10000] ms begin.
2022-08-07 17:57:51.282 INFO 17292 --- [cTaskExecutor-1] c.t.o.springEvent.EmailListener : thread sleep [20000] ms begin.
2022-08-07 17:58:01.293 INFO 17292 --- [ main] c.t.o.springEvent.LogListener : thread sleep [10000] ms end.
2022-08-07 17:58:01.293 INFO 17292 --- [ main] c.t.o.springEvent.OrderPublisher : publish order event end.
2022-08-07 17:58:01.299 INFO 17292 --- [ main] com.test.SpringProjectApplication : Started SpringProjectApplication in 17.591 seconds (JVM running for 18.451)
2022-08-07 17:58:11.290 INFO 17292 --- [cTaskExecutor-1] c.t.o.springEvent.EmailListener : thread sleep [20000] ms end.
此外,还可以使用SmartApplicationListener来设置优先级,匹配事件类型,匹配事件发布者。
import com.test.observerPattern.springEvent.OrderEvent;
import com.test.observerPattern.springEvent.OrderPublisher;
import com.test.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class PriorityListener implements SmartApplicationListener {
private static final Logger LOGGER = LoggerFactory.getLogger(PriorityListener.class);
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return aClass == OrderPublisher.class;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
LOGGER.info("priority listener begin with [{}].", ((OrderEvent) applicationEvent).getName());
TimeUtils.sleep(10000, LOGGER);
}
@Override
public int getOrder() {
// 数字越小,优先级越高
return 0;
}
}
ApplicationListener接口