前言
解耦,是我们一直以来都很重视的事情。而事件,就是我们实现解耦的一大利器。通过观察者模式,我们可以实现事件的发布和订阅之间的解耦。而且在日常开发中,事件的使用场景也是很常见的。比如用户注册完成,可以产生一个事件,以便后续进行操作。或者用户下单,也可以产生一个事件,然后各种监听器对下单这个事件进行自己的处理。这些都是事件机制的使用案例。
这次,我们就来实现Spring中的事件。我们使用Spring框架之后,就可以使用Spring给我们提供的事件发布机制,不需要我们自己去实现了。而且Spring本身也会将自己的一些行为作为事件广播出去,我们可以通过注册相应的监听器,来接收这些事件,从而进行一些自己的处理。
工程结构
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─akitsuki
│ │ │ └─springframework
│ │ │ ├─beans
│ │ │ │ ├─exception
│ │ │ │ │ BeanException.java
│ │ │ │ │
│ │ │ │ └─factory
│ │ │ │ │ Aware.java
│ │ │ │ │ BeanClassLoaderAware.java
│ │ │ │ │ BeanFactory.java
│ │ │ │ │ BeanFactoryAware.java
│ │ │ │ │ BeanNameAware.java
│ │ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ │ DisposableBean.java
│ │ │ │ │ FactoryBean.java
│ │ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ │ InitializingBean.java
│ │ │ │ │ ListableBeanFactory.java
│ │ │ │ │
│ │ │ │ ├─config
│ │ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ │ BeanDefinition.java
│ │ │ │ │ BeanDefinitionRegistryPostProcessor.java
│ │ │ │ │ BeanFactoryPostProcessor.java
│ │ │ │ │ BeanPostProcessor.java
│ │ │ │ │ BeanReference.java
│ │ │ │ │ ConfigurableBeanFactory.java
│ │ │ │ │ DefaultSingletonBeanRegistry.java
│ │ │ │ │ PropertyValue.java
│ │ │ │ │ PropertyValues.java
│ │ │ │ │ SingletonBeanRegistry.java
│ │ │ │ │
│ │ │ │ ├─support
│ │ │ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ │ │ AbstractBeanDefinitionReader.java
│ │ │ │ │ AbstractBeanFactory.java
│ │ │ │ │ BeanDefinitionReader.java
│ │ │ │ │ BeanDefinitionRegistry.java
│ │ │ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ │ │ DefaultListableBeanFactory.java
│ │ │ │ │ DisposableBeanAdapter.java
│ │ │ │ │ FactoryBeanRegistrySupport.java
│ │ │ │ │ InstantiationStrategy.java
│ │ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │ │
│ │ │ │ └─xml
│ │ │ │ XmlBeanDefinitionReader.java
│ │ │ │
│ │ │ ├─context
│ │ │ │ │ ApplicationContext.java
│ │ │ │ │ ApplicationContextAware.java
│ │ │ │ │ ApplicationEvent.java
│ │ │ │ │ ApplicationEventPublisher.java
│ │ │ │ │ ApplicationListener.java
│ │ │ │ │ ConfigurableApplicationContext.java
│ │ │ │ │
│ │ │ │ ├─event
│ │ │ │ │ AbstractApplicationEventMulticaster.java
│ │ │ │ │ ApplicationContextEvent.java
│ │ │ │ │ ApplicationEventMulticaster.java
│ │ │ │ │ ContextClosedEvent.java
│ │ │ │ │ ContextRefreshEvent.java
│ │ │ │ │ SimpleApplicationEventMulticaster.java
│ │ │ │ │
│ │ │ │ └─support
│ │ │ │ AbstractApplicationContext.java
│ │ │ │ AbstractRefreshableApplicationContext.java
│ │ │ │ AbstractXmlApplicationContext.java
│ │ │ │ ApplicationContextAwareProcessor.java
│ │ │ │ ClasspathXmlApplicationContext.java
│ │ │ │
│ │ │ ├─core
│ │ │ │ └─io
│ │ │ │ ClasspathResource.java
│ │ │ │ DefaultResourceLoader.java
│ │ │ │ FileSystemResource.java
│ │ │ │ Resource.java
│ │ │ │ ResourceLoader.java
│ │ │ │ UrlResource.java
│ │ │ │
│ │ │ └─util
│ │ │ ClassUtils.java
│ │ │
│ │ └─resources
│ └─test
│ ├─java
│ │ └─com
│ │ └─akitsuki
│ │ └─springframework
│ │ └─test
│ │ │ ApiTest.java
│ │ │
│ │ ├─event
│ │ │ MyEvent.java
│ │ │
│ │ └─listener
│ │ ContextCloseListener.java
│ │ ContextRefreshListener.java
│ │ MyEventListener.java
│ │
│ └─resources
│ spring.xml
子弹:事件定义
要实现事件机制,我们首先得有事件。事件其实就是用来被广播和监听的一个对象,它可以有很多种,在Spring中,我们的事件都继承自一个顶级的事件:ApplicationEvent
。
package com.akitsuki.springframework.context;
import java.util.EventObject;
/**
* 抽象的事件类
*
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:09
*/
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内置的 EventObject
,EventObject
中有一个Object类型的 source
属性,我们可以用这个属性来传递一些我们必要的信息。
那么有了这个顶级的事件,我们就可以细化我们的事件了。对于Spring来说,ApplicationContext
无疑是极为重要的一部分,针对于它的操作也很多,所以我们需要基于它,来构造出一系列事件。
package com.akitsuki.springframework.context.event;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.ApplicationEvent;
/**
* applicationContext的事件
*
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:10
*/
public abstract 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);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
可以看到,我们在这个事件中,将 ApplicationContext
作为需要传递的信息。而且提供了一个获取context的方法。
接下来,在前面的学习中,我们也了解到,ApplicationContext
会有刷新和关闭操作。而在这些动作发生时,我们也希望能有一些事件能够发布出来,所以还需要下面的这两个事件。
package com.akitsuki.springframework.context.event;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:15
*/
public class ContextRefreshEvent extends ApplicationContextEvent {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public ContextRefreshEvent(Object source) {
super(source);
}
}
package com.akitsuki.springframework.context.event;
/**
* 上下文关闭事件
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:13
*/
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);
}
}
很好理解,分别是针对刷新和关闭动作的事件,继承自 ApplicationContextEvent
,意味着我们可以从这些事件中,获取到 ApplicationContext
对象。
枪:事件发布者
如果说事件是我们用来发射的子弹,那么事件发布者就是我们的枪。
package com.akitsuki.springframework.context;
/**
* Application事件的发布者
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:14
*/
public interface ApplicationEventPublisher {
/**
* 发布事件
* @param applicationEvent 事件
*/
void publishEvent(ApplicationEvent applicationEvent);
}
可以看到,我们的这把枪,可以用来发射 ApplicationEvent
类型的子弹。实际上对于我们上面的实现来说,也就是全部的子弹。因为其他类型的事件,都是继承自 ApplicationEvent
。
靶子:事件监听器
有了子弹和枪,我们自然还需要一个靶子,也就是用来接受事件的对象。
package com.akitsuki.springframework.context;
import java.util.EventListener;
/**
* Spring的事件监听器
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:18
*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
*处理事件
* @param event 事件
*/
void onApplicationEvent(E event);
}
可以看到,这里的监听器需要通过泛型来指定自己要监听对象的类型,而Spring又是如何通过泛型,来找到对应的监听器的,会在下面进行介绍。
靶场管理员:事件广播器
我们有各种各样的监听器,也有各种各样的对象。现在我们需要一个类来帮助我们管理它们,就是事件广播器。
package com.akitsuki.springframework.context.event;
import com.akitsuki.springframework.context.ApplicationEvent;
import com.akitsuki.springframework.context.ApplicationListener;
/**
* Application事件广播器
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:17
*/
public interface ApplicationEventMulticaster {
/**
* 添加一个监听器
* @param listener 监听器
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 移除一个监听器
* @param listener 监听器
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 广播事件
* @param event 事件
*/
void multicastEvent(ApplicationEvent event);
}
可以看到,我们可以根据这个广播器,来添加或者移除一个监听器,也可以通过它来进行事件的发送。这些都很好理解。对于添加/移除监听器来说,这些操作属于通用型操作。而对于广播事件而言,我们可能会有各种不同的实现,比如是否用线程、按照什么顺序等等。所以我们需要一个抽象类,来实现这些通用的功能。
package com.akitsuki.springframework.context.event;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.BeanFactoryAware;
import com.akitsuki.springframework.context.ApplicationEvent;
import com.akitsuki.springframework.context.ApplicationListener;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
* 抽象的application事件广播处理
* @author ziling.wang@hand-china.com
* @date 2022/12/1 15:23
*/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
private final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
this.beanFactory = beanFactory;
}
@Override
@SuppressWarnings("unchecked")
public void addApplicationListener(ApplicationListener<?> listener) {
applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
applicationListeners.remove(listener);
}
/**
* 获取所有支持此事件的listener
* @param event 事件
* @return
*/
protected Collection<ApplicationListener<ApplicationEvent>> getApplicationListeners(ApplicationEvent event) {
List<ApplicationListener<ApplicationEvent>> supportListeners = new LinkedList<>();
for(ApplicationListener<ApplicationEvent> listener : applicationListeners) {
if (supportsEvent(listener, event)) {
supportListeners.add(listener);
}
}
return supportListeners;
}
/**
* 判断listener是否支持事件
* 主要逻辑为拿到Listener泛型中的类型与事件的Class进行比较
* @param listener 监听器
* @param event 事件
* @return
*/
@SuppressWarnings("rawtypes")
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> listener, ApplicationEvent event) {
Class<? extends ApplicationListener> listenerClass = listener.getClass();
//如果是被cglib动态代理过的,类名会有$$,所以要取superclass才是真正的类名
Class<?> targetClass = listenerClass.getName().contains("$$") ? listenerClass.getSuperclass() : listenerClass;
Type genericInterface = targetClass.getGenericInterfaces()[0];
Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
String className = actualTypeArgument.getTypeName();
Class<?> eventClass;
try {
eventClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeanException("no event class:" + className, e);
}
return eventClass.isAssignableFrom(event.getClass());
}
}
挺复杂的一个抽象类,我们来逐一分析。
首当其冲可以看到,它实现了 BeanFactoryAware
接口,从而可以很方便的拿到 BeanFactory
。前面学到的知识,在这里就用上了。
接下来我们看到,它用了一个Set来维护所有注册的监听器,对于添加和删除两个功能的实现也很简单,无非是添加/删除到Set中。但是下面有大量的篇幅,用于另外两个方法的实现:获取事件对应的监听器,以及判断监听器是否支持某个事件。这里是重点,也就是我们上面提到过的,如何通过泛型来拿到监听器需要监听的事件。
重点的实现是在 supportsEvent
中,通过 getGenericInterfaces
方法,拿到监听器的泛型,再判断事件的类型是否属于这个泛型。假设我们的监听器泛型为 ApplicationContextEvent
,那么他就可以监听到 ApplicationContextEvent
、ContextRefreshEvent
、ContextCloseEvent
这三种事件。
有了抽象类,我们还需要一个默认的实现,来帮我们填上 multicastEvent
的坑。
package com.akitsuki.springframework.context.event;
import com.akitsuki.springframework.context.ApplicationEvent;
import com.akitsuki.springframework.context.ApplicationListener;
/**
* Spring事件广播器普通实现
*
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:21
*/
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(ApplicationEvent event) {
for (ApplicationListener<ApplicationEvent> listener : getApplicationListeners(event)) {
listener.onApplicationEvent(event);
}
}
}
很简单的一个遍历调用。但这样做实际上并没有实现解耦,因为事件的发布和监听,是在同一个线程中进行的,可以想象,事件在发布后,必须等待监听处理完成才能继续完成其他操作。实际上Spring在这里是用多线程异步来实现的,这样也就真正的完成了解耦。但这里为了简单起见,就这么简单处理了。
到这儿,我们的事件机制其实已经完成的差不多了。但是我们上面定义的容器刷新和关闭的事件,还没有真正的用起来。所以我们还需要进行一些小改造,来让这些事件能够真正发布出去。
在此之前,我们需要扩充一下ApplicationContext接口的功能。之前的接口对于现在的体量来说,功能太过于狭小了一些。
package com.akitsuki.springframework.context;
import com.akitsuki.springframework.beans.factory.HierarchicalBeanFactory;
import com.akitsuki.springframework.beans.factory.ListableBeanFactory;
import com.akitsuki.springframework.core.io.ResourceLoader;
/**
* 上下文接口
*
* @author ziling.wang@hand-china.com
* @date 2022/11/10 14:15
*/
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader, ApplicationEventPublisher {
}
很简单,只需要多继承一些其他的接口,功能自然就来了。这就是优秀设计的好处。而且我们需要注意到的是,这里也继承了事件发布的接口,意味着它可以实现事件发布功能。接下来让我们看看如何真正发布事件吧。
package com.akitsuki.springframework.context.support;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.akitsuki.springframework.context.ApplicationEvent;
import com.akitsuki.springframework.context.ApplicationEventPublisher;
import com.akitsuki.springframework.context.ApplicationListener;
import com.akitsuki.springframework.context.ConfigurableApplicationContext;
import com.akitsuki.springframework.context.event.ApplicationEventMulticaster;
import com.akitsuki.springframework.context.event.ContextClosedEvent;
import com.akitsuki.springframework.context.event.ContextRefreshEvent;
import com.akitsuki.springframework.context.event.SimpleApplicationEventMulticaster;
import com.akitsuki.springframework.core.io.DefaultResourceLoader;
import java.util.Map;
/**
* 应用上下文类抽象实现
* 继承DefaultResourceLoader是为了处理配置文件资源的加载
* 实现了ConfigurableApplicationContext接口的刷新方法
*
* @author ziling.wang@hand-china.com
* @date 2022/11/10 15:06
*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
private ApplicationEventMulticaster applicationEventMulticaster;
@Override
public void refresh() throws BeanException {
//创建beanFactory,加载beanDefinition
refreshBeanFactory();
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//添加ApplicationContextAwareProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//在bean实例化之前,执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
//初始化事件发布者
initApplicationEventMulticaster();
//注册事件监听器
registerListeners();
//提前实例化单例bean对象
beanFactory.preInstantiateSingletons();
//结束刷新
finishRefresh();
}
/**
* 初始化事件广播
*/
private void initApplicationEventMulticaster() {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
getBeanFactory().addSingleton("applicationEventMulticaster", applicationEventMulticaster);
}
/**
* 注册监听器
*/
private void registerListeners() {
for (ApplicationListener<?> listener : getBeansOfType(ApplicationListener.class).values()) {
applicationEventMulticaster.addApplicationListener(listener);
}
}
/**
* 结束刷新,这里会发布结束刷新事件
*/
private void finishRefresh() {
publishEvent(new ContextRefreshEvent(this));
}
@Override
public void close() {
//发布容器关闭事件
publishEvent(new ContextClosedEvent(this));
//销毁单例对象
getBeanFactory().destroySingletons();
}
@Override
public void publishEvent(ApplicationEvent applicationEvent) {
applicationEventMulticaster.multicastEvent(applicationEvent);
}
}
这里只列出了必要的部分代码,可以看到,在刷新方法中,加入了广播器、监听器的初始化过程,在刷新结束的时候,会发送一个刷新的事件出去。同样的,在关闭的时候,也会发送一个关闭事件。而且这里也实现了发布事件的方法,也就是调用广播器的 multicatsEvent
来进行。
测试
好了,上面写了那么多,我们总算是完成了Spring中的事件机制,下面就让我们亲自体验一番。
首先,我们来写一个自己的事件:
package com.akitsuki.springframework.test.event;
import com.akitsuki.springframework.context.ApplicationEvent;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:49
*/
public class MyEvent extends ApplicationEvent {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MyEvent(Object source) {
super(source);
}
}
接下来,需要一个针对于这个事件的监听器
package com.akitsuki.springframework.test.listener;
import com.akitsuki.springframework.context.ApplicationListener;
import com.akitsuki.springframework.test.event.MyEvent;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:56
*/
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("收到MyEvent的信息力!内容:" + event.getSource());
}
}
顺便再尝试一下监听容器刷新和关闭
package com.akitsuki.springframework.test.listener;
import com.akitsuki.springframework.context.ApplicationListener;
import com.akitsuki.springframework.context.event.ContextRefreshEvent;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:58
*/
public class ContextRefreshListener implements ApplicationListener<ContextRefreshEvent> {
@Override
public void onApplicationEvent(ContextRefreshEvent event) {
System.out.println("收到容器刷新的消息力!内容:" + event.getSource());
}
}
package com.akitsuki.springframework.test.listener;
import com.akitsuki.springframework.context.ApplicationListener;
import com.akitsuki.springframework.context.event.ContextClosedEvent;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 16:59
*/
public class ContextCloseListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("收到容器关闭的消息力!内容:" + event.getSource());
}
}
最后别忘了将这些监听器注册成bean
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id="myEventListener" class="com.akitsuki.springframework.test.listener.MyEventListener"/>
<bean id="contextRefreshListener" class="com.akitsuki.springframework.test.listener.ContextRefreshListener"/>
<bean id="contextCloseListener" class="com.akitsuki.springframework.test.listener.ContextCloseListener"/>
</beans>
这次我们的老朋友钉子户userService系列,终于不见了(笑)
最终测试类,通过虚拟机钩子来注册关闭方法,同时尝试发送我们自己的事件。
package com.akitsuki.springframework.test;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.event.MyEvent;
import org.junit.Test;
/**
* @author ziling.wang@hand-china.com
* @date 2022/12/1 17:03
*/
public class ApiTest {
@Test
public void test() {
ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
context.registerShutdownHook();
context.publishEvent(new MyEvent("给你看看我的宝贝"));
}
}
测试结果
收到容器刷新的消息力!内容:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@4cdbe50f
收到MyEvent的信息力!内容:给你看看我的宝贝
收到容器关闭的消息力!内容:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@4cdbe50f
Process finished with exit code 0
可以看到,成功的收到了消息,这次的练习也圆满完成了。
相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring
,这里对应的代码是mini-spring-10