观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,其主要解决一个对象状态改变给其他关联对象通知的问题,保证易用和低耦合。更多观察者模式的资料:观察者模式
一个典型的应用场景是:当用户注册以后,需要给用户发送邮件,发送优惠券等操作,如下图所示。
使用观察者模式后:
UserService 在完成自身的用户注册逻辑之后,仅仅只需要发布一个 UserRegisterEvent 事件,而无需关注其它拓展逻辑。其它 Service 可以自己订阅 UserRegisterEvent 事件,实现自定义的拓展逻辑。Spring的事件机制主要由3个部分组成。
- ApplicationEvent:通过继承它,实现自定义事件。另外,通过它的 source 属性可以获取事件源,timestamp 属性可以获得发生时间。
- ApplicationEventPublisher:通过实现它,来发布变更事件。
- ApplicationEventListener:通过实现它,来监听指定类型事件并响应动作。这里就以上面的用户注册为例,来看看代码示例。首先定义用户注册事件 UserRegisterEvent。
import org.springframework.context.ApplicationEvent;
/**
* @author 方曦
*/
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 事件。
import com.storyhasyou.boc.entity.User;
import com.storyhasyou.boc.event.UserRegisterEvent;
import com.storyhasyou.boc.mapper.UserMapper;
import com.storyhasyou.boc.service.UserService;
import com.storyhasyou.kratos.base.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* <p>
* 用户表 服务实现类
* </p>
*
* @author fangxi
* @since 2020-12-14
*/
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void register(String username) {
log.info("[register][执行用户({}) 的注册逻辑]", username);
applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));
}
}
创建邮箱Service,实现 ApplicationListener 接口,通过 E 泛型设置感兴趣的事件,实现 onApplicationEvent(E event) 方法,针对监听的 UserRegisterEvent 事件,进行自定义处理。
import com.storyhasyou.boc.event.UserRegisterEvent;
import com.storyhasyou.boc.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
/**
* @author 方曦
*/
@Slf4j
@Service
public class MailServiceImpl implements MailService, ApplicationListener<UserRegisterEvent> {
@Override
public void sendEmail(String username) {
log.info("发送邮箱给:{}", username);
}
@Override
public void onApplicationEvent(UserRegisterEvent event) {
this.sendEmail(event.getUsername());
}
}
创建优惠券Service,不同于上面的实现 ApplicationListener 接口方式,在方法上,添加 @EventListener 注解,并设置监听的事件为 UserRegisterEvent。这是另一种使用方式。
import com.storyhasyou.boc.event.UserRegisterEvent;
import com.storyhasyou.boc.service.CouponService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
/**
* @author 方曦
*/
@Slf4j
@Service
public class CouponServiceImpl implements CouponService {
@Override
@EventListener
public void sendCoupon(UserRegisterEvent event) {
log.info("发送优惠券给:{}", event.getUsername());
}
}
看到这里,细心的同学可能想到了发布订阅模式,其实观察者模式于发布订阅还是有区别的,简单来说,发布订阅模式属于广义上的观察者模式,在观察者模式的 Subject 和 Observer 的基础上,引入 Event Channel 这个中介,进一步解耦。图示如下,可以看出,观察者模式更加轻量,通常用于单机,而发布订阅模式相对而言更重一些,通常用于分布式环境下的消息通知场景。
事件对象,也可以不用继承ApplicationEvent
定义一个通用的Event
import lombok.Data;
/**
* @author 方曦
*/
@Data
public class BaseEvent<E> {
private E data;
private Boolean success;
public BaseEvent(E data) {
this(data, true);
}
public BaseEvent(E data, Boolean success) {
this.data = data;
this.success = success;
}
}
- data:事件过程中的数据
- success:是否触发
自定义一个事件,继承这个通用事件
import com.storyhasyou.kratos.base.BaseEvent;
/**
* @author 方曦
*/
public class UserRegisterEvent extends BaseEvent<String> {
public UserRegisterEvent(String data, Boolean success) {
super(data, success);
}
}
发布事件
/**
* <p>
* 用户表 服务实现类
* </p>
*
* @author fangxi
* @since 2020-12-14
*/
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void register(String username) {
log.info("[register][执行用户({}) 的注册逻辑]", username);
applicationEventPublisher.publishEvent(new UserRegisterEvent(username, true));
}
}
监听事件,只能用 @EventListener
注解
import com.storyhasyou.boc.event.UserRegisterEvent;
import com.storyhasyou.boc.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
/**
* @author 方曦
*/
@Slf4j
@Service
public class MailServiceImpl implements MailService {
@Override
public void sendEmail(String username) {
log.info("发送邮箱给:{}", username);
}
@EventListener(condition = "#event.success")
public void onApplicationEvent(UserRegisterEvent event) {
this.sendEmail(event.getData());
}
}
condition = "#event.success"
: 事件里面的success属性,如果是true则这行,false则不执行。
这样效果和上面一样,只不过我们对事件进行了抽象。可以通过参数控制事件是否执行。