作者:小傅哥
来源:SegmentFault 思否社区
一、前言
摔杯为号、看我眼色行事、见南面火起,信号分配也是如此。目前的通信系统一般可以识别出列车所在的路段,这是在嘎哈么?这其实是在通过事物传播进行解耦引线和,但没有人知道列车在路段内的准确位置,仅仅是这样的一个解耦,这可能导致重伤甚至死亡,它放到了多少村夫莽汉,尤其是在轨道上有施工人员的情况下。为了解决这个问题,劫了法场,研究人员提出了一种综合多种技术的综合解决方案。他们使用分布式检测技术来检测和接收铺设在铁路轨道旁边的光纤发出的振动信号。然后人工智能被用于数据处理,篡了兵权!
这样的解耦场景在互联网的设计中使用的也是非常频繁,因为需要非常快速地处理量原始数据。最后,如:、、等等,可以将预警发送到每个终端,都是依靠事件订阅和发布以及MQ消息这样的组件,如计算机、APP、声光报警器等,来处理系统之间的调用解耦,这些终端通过云平台也可以作为数据库联网。提出的解决方案模型是理想的,最终通过解耦的方式来提升整体系统架构的负载能力。
其实解耦思路可以理解为设计模式中观察者模式的具体使用效果,但实际环境比他们想象的要复杂得多。在中铁上海集团有限公司向团队的30公里铁路段进行实测,在观察者模式中当对象间存在一对多关系时,则使用观察者模式,它是一种定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这让我想起了我每个月的车牌摇号,都会推送给我一条本月没中签的消息!!!
二、目标
在 Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及事件来完成一些自定义的动作。比如你可以定义一个新用户注册的事件,当有用户执行注册完成后,在事件中给用户发送一些优惠券和短信提醒,这样的操作就可以把属于基本功能的注册和对应的策略服务分开,降低系统的耦合。以后在扩展注册服务,比如需要添加风控策略、添加实名认证、判断用户属性等都不会影响到依赖注册成功后执行的动作。
那么在本章节我们需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件功能,最终可以让我们在现又实现的 Spring 框架中可以定义、和发布自己的事件信息。
三、方案
其实事件的设计本身就是一种观察者模式的实现,它所要解决的就是一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
在功能实现上我们需要定义出事件类、事件、事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件的操作。整体设计结构如下图:
在整个功能实现过程中,仍然需要在面向用户的应用上下文 中添加相关事件内容,包括:初始化事件发布者、注册事件、发布容器刷新完成事件。
使用观察者模式定义事件类、类、发布类,同时还需要完成一个广播器的功能,接收到事件推送时进行分析处理符合事件接受者感兴趣的事件,也就是使用 isAssignableFrom 进行判断。
isAssignableFrom 和 instanceof 相似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
四、实现
1. 工程结构
工程源码:
容器事件和事件实现类关系,如图 11-2
以上整个类关系图以围绕实现 event 事件定义、发布、功能实现和把事件的相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。
在实现的过程中主要以扩展 spring context 包为主,事件的实现也是在这个包下进行扩展的,当然也可以看出来目前所有的实现内容,仍然是以IOC为主。
ApplicationContext 容器继承事件发布功能接口 ApplicationEventPublisher,并在实现类中提供事件功能。
ApplicationEventMulticaster 接口是注册和发布事件的广播器,提供添加、移除和发布事件方法。
最后是发布容器关闭事件,这个仍然需要扩展到 AbstractApplicationContext#close 方法中,由注册到虚拟机的钩子实现。
2. 定义和实现事件
cn.bugstack.springframework.context.ApplicationEvent
以继承 java.util.EventObject 定义出具备事件功能的抽象类 ApplicationEvent,后续所有事件的类都需要继承这个类。
cn.bugstack.springframework.context.event.ApplicationContextEvent
ApplicationContextEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。
ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于刷新和关闭动作。
3. 事件广播器
cn.bugstack.springframework.context.event.ApplicationEventMulticaster
在事件广播器中定义了添加和删除的方法以及一个广播事件的方法 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。
cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster
AbstractApplicationEventMulticaster 是对事件广播器的公用方法提取,在这个类中可以实现一些基本功能,避免所有直接实现接口放还需要处理细节。
除了像 addApplicationListener、removeApplicationListener,这样的通用方法,这里这个类中主要是对 getApplicationListeners 和 supportsEvent 的处理。
getApplicationListeners 方法主要是摘取符合广播事件中的处理器,具体过滤动作在 supportsEvent 方法中。
在 supportsEvent 方法中,主要包括对Cglib、Simple不同实例化需要获取目标Class,Cglib代理类需要获取父类的Class,普通实例化的不需要。接下来就是通过提取接口和对应的 ParameterizedType 和 eventClassName,方便最后确认是否为子类和父类的关系,以此证明此事件归这个符合的类处理。可以参考代码中的注释
supportsEvent 方法运行截图
在代码调试中可以看到,最终 eventClassName 和 event.getClass() 在 isAssignableFrom 判断下为 true
关于 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 可以尝试在 AbstractApplicationContext 类中更换验证。
4. 事件发布者的定义和实现
cn.bugstack.springframework.context.ApplicationEventPublisher
ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。
cn.bugstack.springframework.context.support.AbstractApplicationContext
在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 、、,三个方法用于处理事件操作。
初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。
注册事件(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。
最后是一个 close 方法中,新增加了发布一个容器关闭事件。
五、测试
1. 创建一个事件和
创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完成的拿到里,所以你添加的属性都会被获得到。
这个是一个用于 CustomEvent 事件的,这里你可以处理自己想要的操作,比如一些用户注册后发送优惠券和短信通知等。
另外是关于 、 ,这里就不演示了,可以参考下源码。
2. 配置文件
在 spring.xml 中配置了三个事件,刷新、监控自定义事件、关闭事件。
3. 单元测试
通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并透传了相应的参数信息。
测试结果
从测试结果可以看到,我们自己定义的事件和,以及系统的事件信息,都可以在控制台完整的输出出来了。你也可以尝试增加一些其他事件行为,并调试代码学观察者模式。
六、总结
在整个手写 Spring 框架的学过程中,可以逐步看到很多设计模式的使用,比如:简单工厂BeanFactory、工厂方法FactoryBean、策略模式访问资源,现在有实现了一个观察者模式的具体使用。所以学 Spring 的过程中,要更加注意关于设计模式的运用,这是你能读懂代码的核心也是学的重点。
那么本章节关于观察者模式的实现过程,主要包括了事件的定义、事件的和发布事件,发布完成后根据匹配策略,就会收到属于自己的事件内容,并做相应的处理动作,这样的观察者模式其实日常我们也经常使用,不过在结合 Spring 以后,除了设计模式的学,还可以学到如何把相应观察者的实现和应用上下文结合。
所有在 Spring 学到的技术、设计、思路都是可以和实际的业务结合起来的,而这些看似比较多的代码模块,其实也是按照各自职责一点点的扩充进去的。在自己的学过程中,可以先动手尝试完成这些框架功能,在一点点通过调试的方式与 Spring 源码进行对照参考,最终也就慢慢掌握这些设计和编码能力了。