共计 13754 个字符,预计需要花费 35 分钟才能阅读完成。
作者:小傅哥
博客:https://bugstack.cn
积淀、分享、成长,让本人和别人都能有所播种!😄
一、前言
能解耦,是如许重要的一件事件!
摔杯为号、看我眼色行事、见南面火起,这是在嘎哈么?这其实是在通过事物流传进行解耦引线和炸弹,仅仅是这样的一个解耦,它放到了多少村夫莽汉,劫了法场,篡了兵权!
这样的解耦场景在互联网开发的设计中应用的也是十分频繁,如:这里须要一个注册实现事件推送音讯
、 用户下单我会发送一个 MQ
、收到我的领取音讯就能够发货了
等等,都是依附事件订阅和公布以及 MQ 音讯这样的组件,来解决零碎之间的调用解耦,最终通过解耦的形式来晋升整体零碎架构的负载能力。
其实解耦思路能够了解为设计模式中观察者模式的具体应用成果,在观察者模式中当对象间存在一对多关系时,则应用观察者模式,它是一种定义对象间的一种一对多的依赖关系,当一个对象的状态产生扭转时,所有依赖于它的对象都失去告诉并被自动更新。这让我想起了我每个月的车牌摇号,都会推送给我一条本月没中签的音讯!!!
二、指标
在 Spring 中有一个 Event 事件性能,它能够提供事件的定义、公布以及监听事件来实现一些自定义的动作。比方你能够定义一个新用户注册的事件,当有用户执行注册实现后,在事件监听中给用户发送一些优惠券和短信揭示,这样的操作就能够把属于基本功能的注册和对应的策略服务离开,升高零碎的耦合。当前在扩大注册服务,比方须要增加风控策略、增加实名认证、判断用户属性等都不会影响到依赖注册胜利后执行的动作。
那么在本章节咱们须要以观察者模式的形式,设计和实现 Spring Event 的容器事件和事件监听器性能,最终能够让咱们在现又实现的 Spring 框架中能够定义、监听和公布本人的事件信息。
三、计划
其实事件的设计自身就是一种观察者模式的实现,它所要解决的就是一个对象状态扭转给其余对象告诉的问题,而且要思考到易用和低耦合,保障高度的合作。
在性能实现上咱们须要定义出事件类、事件监听、事件公布,而这些类的性能须要联合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。整体设计构造如下图:
- 在整个性能实现过程中,依然须要在面向用户的利用上下文
AbstractApplicationContext
中增加相干事件内容,包含:初始化事件发布者、注册事件监听器、公布容器刷新实现事件。 - 应用观察者模式定义事件类、监听类、公布类,同时还须要实现一个播送器的性能,接管到事件推送时进行剖析解决合乎监听事件接受者感兴趣的事件,也就是应用 isAssignableFrom 进行判断。
- isAssignableFrom 和 instanceof 类似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是 Object。如果 A.isAssignableFrom(B)后果是 true,证实 B 能够转换成为 A, 也就是 A 能够由 B 转换而来。
四、实现
1. 工程构造
small-spring-step-10
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── event
│ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ ├── ApplicationContextEvent.java
│ │ │ ├── ApplicationEventMulticaster.java
│ │ │ ├── ContextClosedEvent.java
│ │ │ ├── ContextRefreshedEvent.java
│ │ │ └── SimpleApplicationEventMulticaster.java
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├── ApplicationContextAware.java
│ │ ├── ApplicationEvent.java
│ │ ├── ApplicationEventPublisher.java
│ │ ├── ApplicationListener.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── event
│ ├── ContextClosedEventListener.java
│ ├── ContextRefreshedEventListener.java
│ ├── CustomEvent.java
│ └── CustomEventListener.java
└── ApiTest.java
工程源码 : 公众号「bugstack 虫洞栈」,回复:Spring 专栏,获取残缺源码
容器事件和事件监听器实现类关系,如图 11-2
- 以上整个类关系图以围绕实现 event 事件定义、公布、监听性能实现和把事件的相干内容应用 AbstractApplicationContext#refresh 进行注册和解决操作。
- 在实现的过程中次要以扩大 spring context 包为主,事件的实现也是在这个包下进行扩大的,当然也能够看进去目前所有的实现内容,依然是以 IOC 为主。
- ApplicationContext 容器继承事件公布性能接口 ApplicationEventPublisher,并在实现类中提供事件监听性能。
- ApplicationEventMulticaster 接口是注册监听器和公布事件的播送器,提供增加、移除和公布事件办法。
- 最初是公布容器敞开事件,这个依然须要扩大到 AbstractApplicationContext#close 办法中,由注册到虚拟机的钩子实现。
2. 定义和实现事件
cn.bugstack.springframework.context.ApplicationEvent
public abstract class ApplicationEvent extends EventObject {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ApplicationEvent(Object source) {super(source);
}
}
- 以继承 java.util.EventObject 定义出具备事件性能的抽象类 ApplicationEvent,后续所有事件的类都须要继承这个类。
cn.bugstack.springframework.context.event.ApplicationContextEvent
public class ApplicationContextEvent extends ApplicationEvent {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ApplicationContextEvent(Object source) {super(source);
}
/**
* Get the <code>ApplicationContext</code> that the event was raised for.
*/
public final ApplicationContext getApplicationContext() {return (ApplicationContext) getSource();}
}
cn.bugstack.springframework.context.event.ContextClosedEvent
public class ContextClosedEvent extends ApplicationContextEvent{
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ContextClosedEvent(Object source) {super(source);
}
}
cn.bugstack.springframework.context.event.ContextRefreshedEvent
public class ContextRefreshedEvent extends ApplicationContextEvent{
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ContextRefreshedEvent(Object source) {super(source);
}
}
- ApplicationContextEvent 是定义事件的抽象类,所有的事件包含敞开、刷新,以及用户本人实现的事件,都须要继承这个类。
- ContextClosedEvent、ContextRefreshedEvent,别离是 Spring 框架本人实现的两个事件类,能够用于监听刷新和敞开动作。
3. 事件播送器
cn.bugstack.springframework.context.event.ApplicationEventMulticaster
public interface ApplicationEventMulticaster {
/**
* Add a listener to be notified of all events.
* @param listener the listener to add
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* Multicast the given application event to appropriate listeners.
* @param event the event to multicast
*/
void multicastEvent(ApplicationEvent event);
}
- 在事件播送器中定义了增加监听和删除监听的办法以及一个播送事件的办法
multicastEvent
最终推送工夫音讯也会通过这个接口办法来解决谁该接管事件。
cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
private BeanFactory beanFactory;
@Override
public void addApplicationListener(ApplicationListener<?> listener) {applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {applicationListeners.remove(listener);
}
@Override
public final void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {if (supportsEvent(listener, event)) allListeners.add(listener);
}
return allListeners;
}
/**
* 监听器是否对该事件感兴趣
*/
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
// 依照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,须要判断后获取指标 class
Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
Type genericInterface = targetClass.getGenericInterfaces()[0];
Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
String className = actualTypeArgument.getTypeName();
Class<?> eventClassName;
try {eventClassName = Class.forName(className);
} catch (ClassNotFoundException e) {throw new BeansException("wrong event class name:" + className);
}
// 断定此 eventClassName 对象所示意的类或接口与指定的 event.getClass() 参数所示意的类或接口是否雷同,或是否是其超类或超接口。// isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是 Object。如果 A.isAssignableFrom(B)后果是 true,证实 B 能够转换成为 A, 也就是 A 能够由 B 转换而来。return eventClassName.isAssignableFrom(event.getClass());
}
}
- 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
public interface ApplicationEventPublisher {
/**
* Notify all listeners registered with this application of an application
* event. Events may be framework events (such as RequestHandledEvent)
* or application-specific events.
* @param event the event to publish
*/
void publishEvent(ApplicationEvent event);
}
- ApplicationEventPublisher 是整个一个事件的公布接口,所有的事件都须要从这个接口公布进来。
cn.bugstack.springframework.context.support.AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
private ApplicationEventMulticaster applicationEventMulticaster;
@Override
public void refresh() throws BeansException {
// 6. 初始化事件发布者
initApplicationEventMulticaster();
// 7. 注册事件监听器
registerListeners();
// 9. 公布容器刷新实现事件
finishRefresh();}
private void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();
applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
}
private void registerListeners() {Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
for (ApplicationListener listener : applicationListeners) {applicationEventMulticaster.addApplicationListener(listener);
}
}
private void finishRefresh() {publishEvent(new ContextRefreshedEvent(this));
}
@Override
public void publishEvent(ApplicationEvent event) {applicationEventMulticaster.multicastEvent(event);
}
@Override
public void close() {
// 公布容器敞开事件
publishEvent(new ContextClosedEvent(this));
// 执行销毁单例 bean 的销毁办法
getBeanFactory().destroySingletons();
}
}
- 在形象利用上下文 AbstractApplicationContext#refresh 中,次要新增了
初始化事件发布者
、注册事件监听器
、公布容器刷新实现事件
,三个办法用于处理事件操作。 - 初始化事件发布者(initApplicationEventMulticaster),次要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件播送器。
- 注册事件监听器(registerListeners),通过 getBeansOfType 办法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
- 公布容器刷新实现事件(finishRefresh),公布了第一个服务器启动实现后的事件,这个事件通过 publishEvent 公布进来,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 办法。
- 最初是一个 close 办法中,新减少了公布一个容器敞开事件。
publishEvent(new ContextClosedEvent(this));
五、测试
1. 创立一个事件和监听器
cn.bugstack.springframework.test.event.CustomEvent
public class CustomEvent extends ApplicationContextEvent {
private Long id;
private String message;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public CustomEvent(Object source, Long id, String message) {super(source);
this.id = id;
this.message = message;
}
// ...get/set
}
- 创立一个自定义事件,在事件类的构造函数中能够增加本人的想要的入参信息。这个事件类最终会被实现的拿到监听里,所以你增加的属性都会被取得到。
cn.bugstack.springframework.test.event.CustomEventListener
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {System.out.println("收到:" + event.getSource() + "音讯; 工夫:" + new Date());
System.out.println("音讯:" + event.getId() + ":" + event.getMessage());
}
}
- 这个是一个用于监听 CustomEvent 事件的监听器,这里你能够解决本人想要的操作,比方一些用户注册后发送优惠券和短信告诉等。
- 另外是对于
ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>
、ContextClosedEventListener implements ApplicationListener<ContextClosedEvent>
监听器,这里就不演示了,能够参考下源码。
2. 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="cn.bugstack.springframework.test.event.ContextRefreshedEventListener"/>
<bean class="cn.bugstack.springframework.test.event.CustomEventListener"/>
<bean class="cn.bugstack.springframework.test.event.ContextClosedEventListener"/>
</beans>
- 在 spring.xml 中配置了三个事件监听器,监听刷新、监控自定义事件、监听敞开事件。
3. 单元测试
public class ApiTest {
@Test
public void test_event() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "胜利了!"));
applicationContext.registerShutdownHook();}
}
- 通过应用 applicationContext 新减少的公布事件接口办法,公布一个自定义事件 CustomEvent,并透传了相应的参数信息。
测试后果
刷新事件:cn.bugstack.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$440a36f5
收到:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@71c7db30 音讯; 工夫:22:32:50
音讯:1019129009086763: 胜利了!敞开事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d
Process finished with exit code 0
- 从测试后果能够看到,咱们本人定义的事件和监听,以及监听系统的事件信息,都能够在控制台残缺的输入进去了。你也能够尝试减少一些其余事件行为,并调试代码学习观察者模式。
六、总结
- 在整个手写 Spring 框架的学习过程中,能够逐渐看到很多设计模式的应用,比方:简略工厂 BeanFactory、工厂办法 FactoryBean、策略模式拜访资源,当初有实现了一个观察者模式的具体应用。所以学习 Spring 的过程中,要更加留神对于设计模式的使用,这是你能读懂代码的外围也是学习的重点。
- 那么本章节对于观察者模式的实现过程,次要包含了事件的定义、事件的监听和公布事件,公布实现后依据匹配策略,监听器就会收到属于本人的事件内容,并做相应的解决动作,这样的观察者模式其实日常咱们也常常应用,不过在联合 Spring 当前,除了设计模式的学习,还能够学到如何把相应观察者的实现和利用上下文联合。
- 所有在 Spring 学习到的技术、设计、思路都是能够和理论的业务开发联合起来的,而这些看似比拟多的代码模块,其实也是依照各自职责一点点的裁减进去的。在本人的学习过程中,能够先入手尝试实现这些框架性能,在一点点通过调试的形式与 Spring 源码进行对照参考,最终也就缓缓把握这些设计和编码能力了。
七、系列举荐
- 学习 Spring,要从设计模式开始!
- 与 Spring 深度关联的中间件学习
- 大学毕业要写多少行代码,能力不必花钱培训就找到一份开发工作?
- 对于 Spring 源码中 getBean 的全流程源码解析
- 基于 jdbc 实现一个 Demo 版的 Mybatis