之前一直说什么观察者模式观察者模式, 其实没有那么难, 今天这篇文章我让你彻底明白什么是观察者模式, 然后灵活应用,同时呢也加深下以前自己对观察者模式的理解哈,好废话说太多了, 干就完事
观察者模式定义
对象之间存在一对多的依赖关系, 当你一(一代表的发起源)这边发生状态变更的时候就会触发(也可以说通知)到多(多指的是多个观察者)这边做出相应的业务执行
定义三个名词
- 数据源
- 发起数据源动作的人(发布者)
- 观察者
啥是数据源?
就是需要准备好的数据,一般都是我们封装好的实体类,或者是DTO类
,Request类
等等
啥是发起数据源动作的人?
简单说就是发布公告的人,其实就是定义一个方法调用具体 观察者的业务处理方法 ,这个时候你就需要把我们上述创建好的数据源
发送个具体观察者做一序列的业务处理, 此实现体现Java高内聚,低耦合思想
啥是观察者?
这个就不用说了吧, 就是我们实现某一块具体业务的类
案例演示
我们这里准备有很多类型的流水, 每一种类型的流水对应发送给不同的组 , 该组会对这种类型的流水做特定的业务处理
定义一个流水实体类
上面所说的数据源
就是TransactionDataDTO
,这会发送给我们的观察者
@Data
public class TransactionData implements Serializable {
public Integer locationId;
public String locationName;
public Integer stockNum;
public TransactionData(Integer locationId, Integer stockNum,String locationName) {
this.locationId = locationId;
this.locationName = locationName;
this.stockNum = stockNum;
}
public class TransactionDataDTO implements Serializable {
private String transType;
private TransactionData data;
public TransactionDataDTO(String transType, TransactionData data) {
this.transType = transType;
this.data = data;
}
}
发起数据源动作的人(发布者)
发布者将会把观察者绑定起来, 然后调用观察者的具体方法执行业务,这里我们可以将公共的绑定和拆分方法提取到抽象方法
public interface SendTxDataPublisher {
void addObserver(RecTxDataObserver observer);
void removeObServer(RecTxDataObserver observer);
void notifyObservers();
}
//抽取公共的绑定和删除方法
@Data
public abstract class AbstractSendTxDataPublisher implements SendTxDataPublisher {
private List<RecTxDataObserver> observers = new ArrayList<>();
@Override
public void addObserver(RecTxDataObserver observer) {
observers.add(observer);
}
@Override
public void removeObServer(RecTxDataObserver observer) {
observers.remove(observer);
}
public abstract void notifyObservers();
}
//发布数据源数据的人
@Data
public class RealSendTxDataPublisher extends AbstractSendTxDataPublisher {
private TransactionDataDTO source;
public RealSendTxDataPublisher(TransactionDataDTO source) {
this.source = source;
}
@Override
public void notifyObservers() {
this.getObservers().forEach(e->e.doExcute(this));
}
}
定义观察者
观察者要开始执行自己特定的业务逻辑处理, 这里定义了两个观察者, 一个Fms观察者, 一个是Pms观察者,他们会根据传入的transType
做一定给数据源过滤,然后执行自己的业务
public interface RecTxDataObserver {
void doExcute(RealSendTxDataPublisher source);
}
//Fms观察者
public class RecTxDataFmsObserver implements RecTxDataObserver {
private RealSendTxDataPublisher source;
private static final String[] TYPEs = {
TransTypeEnum.Order_InStall.name(),
TransTypeEnum.Delivery.name()
};
public RecTxDataFmsObserver(RealSendTxDataPublisher source) {
this.source = source;
}
@Override
public void doExcute(RealSendTxDataPublisher source) {
TransactionDataDTO dto = source.getSource();
TransactionData data = source.getSource().getData();
if (Arrays.asList(TYPEs).contains(dto.getTransType())) {
System.out.println("财务组:接收到数据类型是:transType="+dto.getTransType()+":仓库名称是:"+data.getLocationName()+",仓库ID是:"+data.getLocationId());
System.out.println("开始执行财务组的业务...");
}
}
}
Pms观察者
public class RecTxDataPmsObserver implements RecTxDataObserver {
private RealSendTxDataPublisher source;
/**
* 采购组只接受这几种类型的业务处理,比上述多处理一种Order_Received的业务
*/
private static final String[] TYPEs = {
TransTypeEnum.Order_InStall.name(),
TransTypeEnum.Delivery.name(),
TransTypeEnum.Order_Received.name()
};
public RecTxDataPmsObserver(RealSendTxDataPublisher source) {
this.source = source;
}
@Override
public void doExcute(RealSendTxDataPublisher source) {
TransactionDataDTO dto = source.getSource();
TransactionData data = source.getSource().getData();
if (Arrays.asList(TYPEs).contains(dto.getTransType())) {
System.out.println("采购组:接收到数据类型是:transType="+dto.getTransType()+":仓库名称是:"+data.getLocationName()+",仓库ID是:"+data.getLocationId());
System.out.println("开始执行采购组的业务...");
}
}
}
service
中调用观察者(相当于触发了事件)
public class TransactionServiceImpl implements TransacationService {
@Override
public void sendTransactionData() {
TransactionDataDTO source = new TransactionDataDTO(TransTypeEnum.Delivery.name(),
new TransactionData(11856,100, "上海保养仓库"));
RealSendTxDataPublisher publisher = new RealSendTxDataPublisher(source);
//发送给财务组,财务自己玩自己的处理
RecTxDataFmsObserver fmsObserver = new RecTxDataFmsObserver(publisher);
//发送给采购组,采购自己玩自己的处理
RecTxDataPmsObserver pmsObserver = new RecTxDataPmsObserver(publisher);
publisher.addObserver(fmsObserver);
publisher.addObserver(pmsObserver);
publisher.notifyObservers();
}
}
优点:观察者与被观察者之间是一个轻度的关联关系,体现了模块的高内聚,低耦合。因此对于观察者和被观察者来说,要进行扩展就比较简单了。被观察者并不需要认识任何一个具体的观察者。它只需要提供一个观察者共同的接口即可。提高代码的可复用性。
缺点:在数据源变化,观察者通知被观察者的时候,如果观察者过多,那么可能导致消息的传送失去实时性。
Spring中的ApplicationEvent事件,观察者模式
- 事件源(也就是上述所属数据源)
- 发起事件的人
- 监听者(上述的观察者)
创建事件源
Spring中创建事件源需要继承类ApplicationEvent
, 并且还要注意一定要声明显示构造方法为什么呢? 因为在Spring底层反射获取实例是用的构造方法生成的, 下面是我声明的一个事件源
@Getter
public class SendMqRequestEvent extends ApplicationEvent {
private SendMqRequest request;
//避免外面调用使用new方法生成对象,为什么,纯粹装逼,想想spring吧
public static SendMqRequestEvent crateEvent(Object source,SendMqRequest request) {
return new SendMqRequestEvent(source, request);
}
public SendMqRequestEvent(Object source,SendMqRequest request) {
super(source);
this.request = request;
}
}
监听者(上述观察者)
1.Spring中创建监听者会使用到SmartApplicationListener
, 或者接口ApplicationListener
, 两个类各有各的好处 , 不过这里我们使用SmartApplicationListener
类创建Listener, 这里还需要注意一点, Spring中的监听器是有顺序执行的, 所以你还需要实现排序功能可以用注解@Order
,或者实现接口Ordered
, 监听器中执行方法也可以使用异步执行,看情况而定
2.这里我们创建两个监听器作为案例,测试他们的排序
功能, 注意SendMqRequestRunListener
中getOrder方法return的值
3.这里监听器执行的条件可通过supportsSourceType
,supportsEventType
这两个类进行过滤执行,监听器可以通过事件源获取到需要处理的数据
@Service
public class SendMqRequestRunListener implements SmartApplicationListener, Ordered {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return eventType == SendMqRequestEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return true;
}
@Override
@Async
public void onApplicationEvent(ApplicationEvent event) {
String source = (String) event.getSource();
//int a = 10/0;
System.out.println("SendMqRequestRunListener我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+source);
SendMqRequestEvent event1 = (SendMqRequestEvent) event;
System.out.println("我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+event1.getRequest().getTransObjectId());
}
@Override
public int getOrder() {
return 2;
}
}
@Service
public class SendMqRequestRunListener2 implements SmartApplicationListener, Ordered {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return eventType == SendMqRequestEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
return true;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
String source = (String) event.getSource();
System.out.println("SendMqRequestRunListener2我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+source);
SendMqRequestEvent event1 = (SendMqRequestEvent) event;
System.out.println("我要在这里做一些处理事件处理哦, 首先获取到的数据如下所示:"+event1.getRequest().getTransObjectId());
}
@Override
public int getOrder() {
return 1;
}
}
发布事件的人
1.为什么这个我要弄到后面讲呢? 因为这个发布者和之前上述的的发布者不太一样, Spring中年的发布者需要借用ApplicationEventPublisher
,或者ApplicationContext
类, 不是直接把发布事件这个动作绑定给事件源的发布者
2.这里你需要准备好数据源,然后把数据源发送给监听器,监听器获取数据源之后做一定的处理
@Override
@DBMaster
public void sysFmsData(@RequestBody SendMqRequest request) {
publisher.publishEvent(SendMqRequestEvent.crateEvent(EventSource.SyncPuLiSiTong_NotExisted,request));
}
}
上述我们将 了下 Spring的事件机制是通过观察者模式实现的, 有人可能会问SpringBoot的呢? 其实也是一样的,接下来我们就讲讲SpringBoot的事件机制吧
SprinBoot事件,观察者模式
SpringBoot事件主要实现SpringApplicationRunListener
接口
里面包含七个方法,这七个 方法也可以说是SpringBoot的这个生命周期, 如下所示,七个方法在什么时候执行这个先不讲
public class SpringBootSelfRunListener implements SpringApplicationRunListener {
public SpringBootSelfRunListener(SpringApplication application, String[] args) { }
@Override
public void starting() { System.out.println("hellow.....................gwm"); }
@Override
public void environmentPrepared(ConfigurableEnvironment environment) { }
@Override
public void contextPrepared(ConfigurableApplicationContext context) { }
@Override
public void contextLoaded(ConfigurableApplicationContext context) { }
@Override
public void started(ConfigurableApplicationContext context) { }
@Override
public void running(ConfigurableApplicationContext context) { }
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) { }
}
工厂机制SpringFactoriesLoader
加载META-INF/spring.factories
org.springframework.boot.SpringApplicationRunListener=\
com.tuhu.stockcenter.trans.inner.service.impl.SpringBootSelfRunListener
发布事件SimpleApplicationEventMulticaster
类
其实Spring和SpringBoot的事件机制都是用的这个方法发送事件,具体源码还需要自己看看