观察者模式 Observer
现实中的观察者模式:
一个拥有众多粉丝的明星: 每个粉丝都想了解自己喜欢的明星的所有最新动态。 因此,只要对明星感兴趣,粉丝就可以关注该明星,当有新的动态时,会通知所有关注的粉丝; 当失去兴趣时,就取消关注该明星。
在这里,可以将粉丝视为观察者,将明星视为主题。
一、概念
GoF 定义: 定义对象之间的_一对多_依赖关系,以便一个对象改变状态时,所有依赖它的对象都会收到通知并自动更新。
在这种模式下,有多个观察者(对象)在观察某个特定的主题(对象)。 这种模型也被成为_发布-订阅模型_
- 如果观察者对主题感兴趣,并且希望在主题内部发生变化时得到通知,则将自己注册到该主题;
- 如果观察者对主题失去兴趣,只需要从主题上取消注册即可。
核心概念
- 主题(Subject): 被观察的对象,当状态发生变化时,通知所有注册过的观察者;
- 观察者(Observer): 观察者主题的对象;
观察者模式的优点:
- 解耦合:将主题与观察者分离,降低耦合
- 支持动态注册和取消注册:观察者可以随时动态注册或取消注册,灵活管理依赖关系
- 广播通知:主体维护一个观察者列表,可以向所有观察者广播通知
UML 对模式的描述
代码实现:
interface IObserver {
void update(int i);
}
class Observer1 implements IObserver {
@Override
public void update(int i) {
System.out.println("Observer1: myValue in Subject is now: " + i);
}
}
interface ISubject {
void register(IObserver o);
void unregister(IObserver o);
void notifyObservers(int i);
}
class Subject implements ISubject {
List<IObserver> observersList = new ArrayList<IObserver>();
private int myValue;
public int getMyValue() {
return myValue;
}
public void setMyValue(int myValue) {
this.myValue = myValue;
// Notify observers
notifyObservers(myValue);
}
@Override
public void register(IObserver o) {
observersList.add(o);
}
@Override
public void unregister(IObserver o) {
observersList.remove(o);
}
@Override
public void notifyObservers(int updatedValue) {
for (int i = 0; i < observersList.size(); i++) {
observersList.get(i).update(updatedValue);
}
}
}
优化:将主题的通知方法设置为保护范围,子类在自己关心的领域中调用父类的通知方法实现功能。
代码实现:
interface IObserver {
void update(String s, int i);
}
class Observer1 implements IObserver {
@Override
public void update(String s, int i) {
System.out.println("Observer1: myValue in " + s + " is now: " + i);
}
}
interface ISubject {
void register(IObserver o);
void unregister(IObserver o);
}
abstract class AbstractSubject implements ISubject {
List<IObserver> observersList = new ArrayList<>();
@Override
public void register(IObserver o) {
observersList.add(o);
}
@Override
public void unregister(IObserver o) {
observersList.remove(o);
}
protected void notifyObservers(int updatedValue) {
for (int i = 0; i < observersList.size(); i++) {
observersList.get(i).update(this.getClass().getSimpleName(), updatedValue);
}
}
}
class Subject1 extends AbstractSubject {
private int myValue;
public int getMyValue() {
return myValue;
}
public void setMyValue(int myValue) {
this.myValue = myValue;
// Notify observers
notifyObservers(myValue);
}
}
二、 实现观察者
- 定义使用的域对象
public record OrderCreateEvent(String orderId) {
}
- 定义观察者接口:
public interface Observer<T> {
void onEvent(T event);
}
- 定义 Email 通知:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EmailNotificationService implements Observer<OrderCreateEvent> {
@Override
public void onEvent(OrderCreateEvent event) {
// 发送邮件通知
log.info("Sending email notification for order: {}", event.orderId());
}
}
- 定义持久化功能:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class InventoryUpdateService implements Observer<OrderCreateEvent> {
@Override
public void onEvent(OrderCreateEvent event) {
// 更新库存信息
log.info("Updating inventory for order: {}", event.orderId());
}
}
- 定义主题接口:
public interface Subject<T> {
void register(Observer<T> o);
void unregister(Observer<T> o);
}
- 定义主题抽象类:
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractSubject<T> implements Subject<T> {
private final List<Observer<T>> observers = new ArrayList<>();
@Override
public void register(Observer<T> o) {
observers.add(o);
}
@Override
public void unregister(Observer<T> o) {
observers.remove(o);
}
protected void notifyObservers(T event) {
observers.forEach(observers -> observers.onEvent(event));
}
}
- 订单主题服务类
import org.springframework.stereotype.Component;
@Component
public class OrderSubjectService extends AbstractSubject<OrderCreateEvent> {
public void createOrder(String orderId) {
OrderCreateEvent event = new OrderCreateEvent(orderId);
// 通知所有观察者
notifyObservers(event);
}
}
- 测试:
@SpringBootTest
class OrderSubjectServiceTest {
@Autowired
private OrderSubjectService orderSubjectService;
@Autowired
private ApplicationContext applicationContext;
@Test
void createOrder() {
EmailNotificationService emailNotificationService = applicationContext.getBean(EmailNotificationService.class);
InventoryUpdateService inventoryUpdateService = applicationContext.getBean(InventoryUpdateService.class);
orderSubjectService.register(emailNotificationService);
orderSubjectService.register(inventoryUpdateService);
orderSubjectService.createOrder("123");
orderSubjectService.unregister(emailNotificationService);
orderSubjectService.createOrder("456");
}
}
- 预期输出:
Sending email notification for order: 123
Updating inventory for order: 123
Updating inventory for order: 456
三、 Spring 的支持
Spring Framework 提供了一套基于观察者模式的事件发布订阅机制,从而实现组件之间的协作:
Spring Framework 的事件发布订阅机制既可以支持普通非事务事件的发布订阅,也可以支持事务下的事件发布订阅。
-
ApplicationEvent: 事件对象父类,具体的事件类继承该类实现; -
ApplicationEventPublisher: 事件发布接口,负责发布事件; -
ApplicationListener/@EventListener: 普通非事务事件监听接口,监听器实现该接口,支持 SpEL 表达式实现基于条件的事件传递; -
@TransactionEventListener: 事务监听接口修饰注解注意
@TransactionEventListener必须与@Transaction配合使用。phase: 使用TransactionPhase设置@TransactionEventListener的phase属性,指定事件监听的事务的阶段。fallbackExecution: 如果设置为true表示普通非事务事件也可以触发
可以使用 @Async 注解修饰事件接收方法,实现异步执行。
普通事件案例
- 定义事件:
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
@Getter
public class UserRegisteredEvent extends ApplicationEvent {
private final User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
}
public record User(String email,
String phoneNumber,
boolean sendSms) {
}
- 发布事件:
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class UserRegisteredService {
private final ApplicationEventPublisher eventPublisher;
public void publishEvent(User user) {
UserRegisteredEvent registeredEvent = new UserRegisteredEvent(this, user);
eventPublisher.publishEvent(registeredEvent);
}
}
- 监听事件
- 实现 ApplicationListener 接口方式:
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class SendEmailListener implements ApplicationListener<UserRegisteredEvent> {
@Override
public void onApplicationEvent(UserRegisteredEvent event) {
User user = event.getUser();
// 发送邮件逻辑
log.info("发送邮件给: {}",user.email());
}
}
- 使用 @EventListener 注解:
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class SendSmsListener {
@EventListener(condition = "#event.user.sendSms() == true ")
public void onApplicationEvent(UserRegisteredEvent event) {
User user = event.getUser();
// 发送短信逻辑
log.info("发送短信给: {}", user.phoneNumber());
}
}
事务事件案例
- 事务监听:
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import static org.springframework.transaction.event.TransactionPhase.*;
@Slf4j
@Component
public class OrderEventListener {
@Async("taskExecutor") // 支持异步执行
@TransactionalEventListener(phase = BEFORE_COMMIT)
public void beforeCommit(OrderCreatedEvent event) {
log.info("before commit order: {}", event.getOrder());
}
@TransactionalEventListener(phase = AFTER_COMMIT)
public void afterCommit(OrderCreatedEvent event) {
log.info("after commit order: {}", event.getOrder());
}
@TransactionalEventListener(phase = AFTER_ROLLBACK)
public void afterRollback(OrderCreatedEvent event) {
log.info("after rollback order: {}", event.getOrder());
}
@TransactionalEventListener(phase = AFTER_COMPLETION)
public void afterCompletion(OrderCreatedEvent event) {
log.info("after completion order: {}", event.getOrder());
}
}
- 发布事件:即事件源
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
@Component
public class OrderTransactionService {
private final ApplicationEventPublisher eventPublisher;
private final OrderRepository orderRepository;
@Transactional(rollbackFor = RuntimeException.class)
public String createOrder(String orderDetails) {
Order order = Order.builder().details(orderDetails).build();
try {
// 模拟事务回退
if (orderDetails.equals("back")) {
throw new RuntimeException("Error");
}
order = orderRepository.save(order);
// 发布订单创建事件
eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
return order.id();
} catch (RuntimeException e) {
eventPublisher.publishEvent(new OrderCreatedEvent(this,order));
throw e;
}
}
}