领域事件

观察者模式

大家在日常业务开发工作中相信多多少少遇到过下面这样的几个场景:

  • 当某一个特定事件或动作发生以后,需要执行很多联动动作,如果串行去执行的话太耗时,如果引入消息中间件的话又太重了;
  • 想要针对不同的传参执行不同的策略,也就是我们常说的策略模式,但 10 个人可能有 10 种不同的写法,夹杂在一起总感觉不那么优雅;
  • 自己的系统想要调用其他系统提供的能力,但其他系统总是偶尔给你一点“小惊喜”,可能因网络问题报超时异常或被调用的某一台分布式应用机器突然宕机,我们想要优雅无侵入式地引入重试机制。

其实上面提到的几个典型业务开发场景 Spring 都为我们提供了很好的特性支持,我们只需要引入 Spring 相关依赖就可以方便快速的在业务代码当中使用啦,而不用引入过多的三方依赖包或自己重复造轮子。观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,其主要解决一个对象状态改变给其他关联对象通知的问题,保证易用和低耦合。一个典型的应用场景是:当用户注册以后,需要给用户发送邮件,发送优惠券等操作,如下图所示。

原有流程

使用观察者模式后:

使用观察者模式后的变化

UserService 在完成自身的用户注册逻辑之后,仅仅只需要发布一个 UserRegisterEvent 事件,而无需关注其它拓展逻辑。其它 Service 可以自己订阅 UserRegisterEvent 事件,实现自定义的拓展逻辑。Spring 的事件机制主要由 3 个部分组成。

  • **ApplicationEvent:**通过继承它,实现自定义事件。另外,通过它的 source 属性可以获取事件源,timestamp 属性可以获得发生时间。
  • **ApplicationEventPublisher:**通过实现它,来发布变更事件。
  • **ApplicationEventListener:**通过实现它,来监听指定类型事件并响应动作。这里就以上面的用户注册为例,来看看代码示例。首先定义用户注册事件 UserRegisterEvent。
public class UserRegisterEvent extends ApplicationEvent {
    /**
     * 用户名
     */
    private String username;
    public UserRegisterEvent(Object source) {
        super(source);
    }
    public UserRegisterEvent(Object source, String username) {
        super(source);
        this.username = username;
    }
    public String getUsername() {
        return username;
    }
}

然后定义用户注册服务类,实现 ApplicationEventPublisherAware 接口,从而将 ApplicationEventPublisher 注入进来。从下面代码可以看到,在执行完注册逻辑后,调用了 ApplicationEventPublisher 的 publishEvent(ApplicationEvent event) 方法,发布了 UserRegisterEvent 事件。

@Service
public class UserService implements ApplicationEventPublisherAware { // <1>
    private Logger logger = LoggerFactory.getLogger(getClass());
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
    public void register(String username) {
        // ... 执行注册逻辑
        logger.info("[register][执行用户({}) 的注册逻辑]", username);
        // <2> ... 发布
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));
    }
}

创建邮箱 Service,实现 ApplicationListener 接口,通过 E 泛型设置感兴趣的事件,实现 onApplicationEvent(E event) 方法,针对监听的 UserRegisterEvent 事件,进行自定义处理。

@Service
public class EmailService implements ApplicationListener<UserRegisterEvent> { // <1>
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    @Async// <3>
    public void onApplicationEvent(UserRegisterEvent event) { // <2>
        logger.info("[onApplicationEvent][给用户({}) 发送邮件]", event.getUsername());
    }
}

创建优惠券 Service,不同于上面的实现 ApplicationListener 接口方式,在方法上,添加 @EventListener 注解,并设置监听的事件为 UserRegisterEvent。这是另一种使用方式。

@Service
publicclass CouponService {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @EventListener// <1>
    public void addCoupon(UserRegisterEvent event) {
        logger.info("[addCoupon][给用户({}) 发放优惠劵]", event.getUsername());
    }
}

简单来说,发布订阅模式属于广义上的观察者模式,在观察者模式的 Subject 和 Observer 的基础上,引入 Event Channel 这个中介,进一步解耦。图示如下,可以看出,观察者模式更加轻量,通常用于单机,而发布订阅模式相对而言更重一些,通常用于分布式环境下的消息通知场景。

观察者模式与发布订阅模式

上一页