技术经验解读:【Java】事件驱动模型和观察者模式

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 技术经验解读:【Java】事件驱动模型和观察者模式

你有一件事情,做这件事情的过程包含了许多职责单一的子过程。这样的情况及其常见。当这些子过程有如下特点时,我们应该考虑设计一种合适的框架,让框架来完成一些业务无关的事情,从而使得各个子过程的开发可以专注于自己的业务。


这些子过程有一定的执行次序;


这些子过程之间需要较灵活的跳转;


这些子过程也许需要围绕同一个上下文做操作;


此时可以考虑使用事件驱动的方式来组织这些子过程,此时这些子过程可以被称之为事件处理器(或监听器),而将事件处理器组织起来的管理者,叫做事件中心。最显而易见的实现方式,是观察者模式,或者监听者模式。作为一个例子,考虑一个消息转发系统,它从上游接收消息,然后转发给正确的下游用户。整个过程可以拆分为消息解析、消息存储、消息发送等步骤。


事件Event


首先定义事件Event。事件将作为一个基本元素,在处理器和事件中心之间建立其连线。这里为了能够统一处理异常。以及针对异常打出日志,除了业务相关的事件,还增加了异常事件和日志事件。当然相应的也应该新增与之对应的事件处理器。


1 package me.test.eventcenter;


2


3 /


4 Created by chng on 2015/12/18.


5 /


6 public class EventName {


7


8 private final String name;


9 public EventName(String name) {


10 this.name = name;


11 }


12


13 public static EventName msg_received = new EventName("msg_received");


14 public static EventName msg_resolved = new EventName("msg_resolved");


15 public static EventName msg_stored = new EventName("msg_stored");


16 public static EventName msg_pushed = new EventName("msg_pushed");


17 public static EventName exception_occured = new EventName("exception_occured");


18 public static EventName end_and_log = new EventName("end_and_log");


19


20 public String getName() {


21 return name;


22 }


23 }


事件处理器 EventHandler


随后,定义一个简单的事件处理器的抽象类,其中包含一个单例的事件中心,每个处理器通过持有这个事件中心来执行注册自己(即订阅一个事件)和呼起下一个事件的操作。


package me.test.eventcenter.handler;


import me.test.eventcenter.EventCenter;


import org.springframework.beans.factory.InitializingBean;


import javax.annotation.Resource;


/


Created by chng on 2015/12/18.


/


public abstract class EventHandler implements InitializingBean {


@Resource


EventCenter eventCenter;


public abstract void handle(Object ... param);


}


事件中心 EventCenter


有了事件和事件处理器,接下来定义一个事件中心,将二者粘起来。


package me.test.eventcenter;


import com.google.common.collect.Lists;


import com.google.common.collect.Maps;


import me.test.eventcenter.handler.EventHandler;


import org.springframework.stereotype.Component;


import org.springframework.util.CollectionUtils;


import java.util.List;


import java.util.Map;


/


Created by chng on 2015/12/18.


/


@Component


public class EventCenter {


private Map

/


向事件中心广播一个时间,驱使事件中心执行该事件的处理器


@param eventName


//代码效果参考:http://www.jhylw.com.cn/325620149.html

@param param

/


public void fire(EventName eventName, Object ... param) {


System.out.println(eventName.getName());


List handlerList = regTable.get(eventName);


if(CollectionUtils.isEmpty(handlerList)) {


// log


return;


}


for(EventHandler handler: handlerList) {


try {


handler.handle(param);


} catch (Exception e) {


fire(EventName.exception_occured, e);


}


}


}


/


将自己注册为事件中心的某个事件的处理器


@param eventName


@param handler


/


public void register(EventName eventName, EventHandler handler) {


List handlerList = regTable.get(eventName);


if(null == handlerList) {


handlerList = Lists.newLinkedList();


}


handlerList.add(handler);


regTable.put(eventName, handlerList);


}


}


在事件中心中,事件和处理器之间的关系表示为一个HashMap,每个事件可以被多个处理器监听,而一个处理器只能监听一个事件(这样的关系并非是固定的,也可在运行时动态地改变)。当呼起一个事件时,事件中心找到该事件的监听者,逐个调用他们的处理方法。将各子模块的执行集中在这里管理,还有两个额外的好处:


1 如果发生异常,则呼起异常处理器。这样,一旦业务模块发生了不得不终止整个过程的时候,不需要自己写try/catch子句,而只需要将异常往上抛,直到抛给框架层,由它来做这些统一的事情。然而这并不意味着各业务模块彻彻底底地摆脱了难看的try/catch/finally,运行时发生的异常被catch后,并非都可以直接END,何去何从仍然视情况而定,直接将异常吞掉也未尝不可能。


2 打日志的活儿交给EventCenter就好了,没人比它更清楚当前执行到了哪一步。而各子模块里面,可以省去许多散布在各处的日志语句。对于散弹式日志的问题,解决方法不止一种,AOP也是个不错的选择。


测试


为了让整个过程跑起来,我们只需要发起一个初始的事件,将所有的事件处理器都依次驱动起来:


/


Created by OurEDA on 2015/12/18.


/


public class TestEventCenter extends BaseTest {


@Resource


EventCenter eventCenter;


@Test


public void test() {


RawMessage rawMessage = new RawMessage("NotifyType: amq");


rawMessage.setType(RawMessage.MessageType.amq);


eventCenter.fire(EventName.msg_received, notify);


}


}


以测试通过为目标,我们开始定义一系列的EventHandler,并将这些Handler注册到合适的事件上。例如一个消息解析的Handler,对msg_receive事件感兴趣,解析完成后将发起msg_store事件,那么:


package me.test.eventcenter.handler;


import me.test.eventcenter.;


import me.test.messagedo.Message;


import me.test.messagedo.RawMessage;


import me.test.resolvers.MsgResolverList;


import org.springframework.beans.factory.InitializingBean;


import org.springframework.stereotype.Component;


import javax.annotation.Resource;


/**


Created by chng on 2015/12/18.


/


@Component


public class MsgResolveHandler extends EventHandler implements InitializingBean {


@Resource


private MsgResolverList resolverList;


@Override


public void handle(Object... param) {


/**


Resolver


/


RawMessage rm = (RawMessage) param【0】;


Message message = resolverList.resolve(rm);


eventCenter.fire(EventName.msg_resolved, message);


}


public void afterPropertiesSet() throws Exception {


eventCenter.register(EventName.msg_received, this);


}


}


可以看到,对象在初始阶段把自己(this)注册到了事件中心里。handler方法则只关心如何解析消息,不需要关系别的事情。针对不同类型的消息,解析器可以写成Map的形式,一种类型对应一个解析器;如果消息的分类比较复杂,还可以写成职责链的形式当然这都无关紧要,我们需要知道的是,这个模块只解析消息,与其他子模块之间是完全解耦的。


例如,一种可能的解析器组合体是这样的:


MsgResolver.java (interface)


package me.test.resolvers;


import me.test.messagedo.Message;


import me.test.messagedo.RawMessage;


/**


Created by OurEDA on 2015/12/18.


/


public interface MsgResolver {


public boolean canResolve(RawMessage rm);


public Message resolve(RawMessage rm);


}


MsgResolverList.java


package me.test.resolvers;


import me.test.messagedo.Message;


import me.test.messagedo.RawMessage;


import org.springframework.stereotype.Component;


import java.util.List;


/**


Created by chng on 2015/12/18.


*/


@Component


public class MsgResolverList implements MsgResolver{


//职责链


private List resolvers;


public List getResolvers() {


return resolvers;


}


public void setResolvers(List resolvers) {


this.resolvers = resolvers;


}


public boolean canResolve(RawMessage rawMessage) {


return true;


}


public Message resolve(RawMessage rawMessage) {


for(MsgResolver resolver: resolvers) {


if(resolver.canResolve(rawMessage)) {


System.out.println("NotifyType: "+rawMessage.type);


return resolver.resolve(rawMessage);


}


}


return null;


}


}


不必额外打日志,用例的输出是这样的:


哪一步出了问题,出了什么问题,通通一目了然。


其他:


1 上下文 Context


各个处理器都围绕一个上下文做处理,此例为了体现通用性,上下文直接用Object表示。在实际的场景下,则需要一个统一的结构体。不同的Handler将对该统一上下文的不同内容感兴趣。


2 线程封闭 ThreadLocal


当有多个线程都在事件中心中进行周转时,还需要考虑线程安全问题,保证线程的调度不会对事件处理器的呼起次序造成干扰。因此整个事件中心和上下文,都需要做隔离。


3 反思


上面这种写法有两个明确的缺点:事件的注册操作写死在每个处理器的初始化代码中,一来缺乏灵活性,二来对于各Handler是如何组织起来的,没有一个统一而清晰的bigmap。

目录
打赏
0
0
0
0
0
分享
相关文章
最新技术栈下 Java 面试高频技术点实操指南详解
本指南结合最新Java技术趋势,涵盖微服务(Spring Cloud Alibaba)、响应式编程(Spring WebFlux)、容器化部署(Docker+Kubernetes)、函数式编程、性能优化及测试等核心领域。通过具体实现步骤与示例代码,深入讲解服务注册发现、配置中心、熔断限流、响应式数据库访问、JVM调优等内容。适合备战Java面试,提升实操能力,助力技术进阶。资源链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)
99 25
2025 年 Java 核心技术从入门到精通实战指南
《2025年Java核心技术实战指南》全面覆盖Java开发的最新趋势与最佳实践。内容包括Java新特性(如模式匹配、文本块、记录类)、微服务架构(Spring Boot 3.0+、Spring Cloud)、响应式编程(Reactor、WebFlux)、容器化与云原生(Docker、Kubernetes)、数据访问技术(JPA、R2DBC)、函数式编程、单元测试与集成测试(JUnit 5、Mockito)、性能优化与监控等。通过实战案例,帮助开发者掌握构建高性能、高可用系统的技能。代码资源可从[链接](https://pan.quark.cn/s/14fcf913bae6)获取。
120 7
Java 基础篇必背综合知识点最新技术与实操应用全面总结指南
本总结梳理了Java 17+的核心知识点与新技术,涵盖基础概念(模块化系统、GraalVM)、数据类型(文本块、模式匹配)、流程控制(增强switch)、面向对象(Record类、密封类)、常用类库(Stream API、HttpClient)、实战案例(文件处理)、构建工具(Maven、Gradle)、测试框架(JUnit 5)、开发工具(IDE、Git)及云原生开发(Spring Boot 3、Docker)。通过理论结合实操,帮助开发者掌握Java最新特性并应用于项目中。代码示例丰富,建议配合实践加深理解。
65 4
|
23天前
|
Java 17 + 特性与现代开发技术实操应用详解
本指南聚焦Java 17+最新技术,涵盖模块化开发、Record类、模式匹配、文本块、Stream API增强、虚拟线程等核心特性,结合Spring Boot 3与Micronaut框架实战。通过实操案例解析现代Java开发技术栈,包括高性能并发编程、GraalVM原生编译及开发工具链配置。同时梳理面试高频考点,助力掌握Java新特性和实际应用,适合学习与项目实践。代码示例丰富,附带完整资源下载链接。
249 0
java 最新技术驱动的智能教育在线实验室设备管理与实验资源优化实操指南
这是一份基于最新技术的智能教育在线实验室设备管理与实验资源优化的实操指南,涵盖系统搭建、核心功能实现及优化策略。采用Flink实时处理、Kafka消息队列、Elasticsearch搜索分析和Redis缓存等技术栈,结合强化学习动态优化资源调度。指南详细描述了开发环境准备、基础组件部署、数据采集与处理、模型训练、API服务集成及性能调优步骤,支持高并发设备接入与低延迟处理,满足教育机构数字化转型需求。代码已提供下载链接,助力快速构建智能化实验室管理系统。
86 44
Java 最新技术实操:从基础到进阶的详细指南
本文介绍了Java 17及后续版本的核心技术实操,涵盖新特性、集合框架、异常处理和多线程编程等内容。主要包括:密封类(Sealed Classes)的继承层级控制、模式匹配(Pattern Matching)简化类型判断、文本块(Text Blocks)处理多行字符串;集合框架中的工厂方法和Stream API高级操作;异常处理的最佳实践如自动资源管理(ARM)和自定义异常;多线程编程中的CompletableFuture异步编程和ReentrantLock显式锁使用。
81 6
|
24天前
|
Java最新技术(JDK 11+) 及以上 Java 最新技术之集合框架实操应用详解
本示例基于Java最新技术(JDK 11+),涵盖集合框架的核心功能,结合Java 8+特性(如Stream API、Lambda表达式)与并发编程最佳实践。内容包括:List操作(初始化、Lambda过滤、Stream处理)、Map操作(流式过滤、ConcurrentHashMap原子操作、并行流)、Set操作(TreeSet排序、CopyOnWriteArraySet并发安全)、Queue/Deque操作(优先队列、双端队列)以及高级聚合操作(集合转换、分组统计、平均值计算)。 [代码下载](https://pan.quark.cn/s/14fcf913bae6)
40 4
最新 Java 从入门到实战技术实操指南
这是一份全面的Java实操指南,涵盖从入门到微服务架构的完整学习路径。内容包括Java 21新特性(虚拟线程、Record类)、响应式编程(Spring WebFlux)、微服务架构(Spring Boot 3.2、Spring Cloud、Kubernetes)、数据库与缓存(Redis 8、R2DBC)以及云原生部署和监控(Prometheus、Grafana)。通过电商系统实战项目,掌握最新技术栈与开发技巧。适合初学者及进阶开发者,附带代码示例与资源链接,助你快速提升技能。
40 0
Java 最新技术实操内容:从基础到进阶的全方位指南
本内容聚焦Java最新技术的实操应用,涵盖模块化开发(JPMS)、响应式编程(Spring WebFlux + Project Reactor)、微服务架构(Spring Cloud)、容器化部署(Docker + Kubernetes)、函数式编程、响应式缓存(Redis Reactive API)、反应式数据库访问(R2DBC)、测试技术(JUnit 5 + Mockito)及性能调优(JFR + JMC)。通过具体案例,如模块化图书管理系统、响应式REST API构建、微服务搭建等,深入讲解核心技术实现与应用场景。适合高并发系统、云原生应用和微服务架构开发者学习参考。
43 0

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问