Spring 拓展原理之事件监听源码级教程
开篇
哈喽,大家好,我是尘心,一个每天都在电脑前酣战淋漓的男人!说起Spring,想必大家都不陌生,但是想要精通Spring各类设计原理,估计还有很长的一段路要走。今天我们来分析一下,Spring拓展原理中的事件监听。事件监听在实际的开发工作当中使用还是比较广泛的,如果你还不知道,那么一起来学习一下吧
注:作者代码版本均使用【4.3.12.RELEASE】
maven 依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
复制代码
系统事件派发【源码剖析】
初探究竟
我们编写一个自己的监听器,用于监听容器中的事件。实际代码如下:
/**
* 使用 Component 注解加入到容器中
*/
@Component
public class MyActionListener implements ApplicationListener<ApplicationEvent> {
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("收到事件:" + event);
}
}
复制代码
运行代码,查看控制台:
从控制台看出,只要容器一启动就会调用onApplicationEvent
方法,触发ApplicationEvent
事件。
奇怪,我们没有发布任何事件,为什么会有事件触发呢?原因是IOC容器一启动就会发布一些基础事件,告知用户,方便用户处理。我们点进去查看一下源代码:
深入探究
1) 这是我们编写的启动类,我们采用的是AnnotationConfigApplicationContext
注解启动IOC容器
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 打印所有容器中bean的名称
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
复制代码
- 跟踪
AnnotationConfigApplicationContext
进去,发现有以下几个方法:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
// 调用构造器
this();
// 根据注解注册一些bean的定义信息
register(annotatedClasses);
// 刷新容器
refresh();
}
复制代码
前两步骤都是一些注册定义等等,没有实际的创建工作,所以我们猜测,事件发布是在刷新容器的时候调用
- 跟踪
refresh()
方法,进去发现它是IOC容器的核心处理
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
复制代码
我们重点看到了 initApplicationEventMulticaster();
初始化事件派发器 和 registerListeners();
注册监听器方法
事件派发器和监听器都准备好了,那么派发事件肯定是在后面。
- 跟踪
finishRefresh();
方法进去,果不其然,在这里调用了publishEvent
发布了ContextRefreshedEvent
事件. 而我们控制台打印的就是这个事件!
/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event. 在这里发布了一个 ContextRefreshedEvent事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
复制代码
- 系统事件除了我们刚才监听的那几个,还有如下几个:
我们刚才使用的是父类事件,系统默认派发的是子类ContextRefreshedEvent 事件。 Spring版本请参考开篇中的版本定义
其他事件都是系统已经定义好的,直接使用就可以啦。下面我们重点说一下,自定义事件的派发和监听!
自定义事件派发【代码实战】
- 编写自己的事件
想要我们自己想要发布事件,其实很简单,编写自定义事件类继承ApplicationEvent
,拿到IOC容器,直接调用派发方法即可
// 这里我们编写一个自定义事件
public class MyApplicationEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the component that published the event (never {@code null})
*/
public MyApplicationEvent(Object source) {
super(source);
}
}
复制代码
- 主动派发自定义事件
使用context 的 publishEvent
方法派发事件
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("发布事件:MyApplicationEvent");
context.publishEvent(new MyApplicationEvent("事件MyApplicationEvent"));
System.out.println("发布事件:MyApplicationEvent2");
context.publishEvent(new MyApplicationEvent("事件MyApplicationEvent2"));
}
}
复制代码
- 监听自定义事件
@Component
public class MyActionListener implements ApplicationListener<MyApplicationEvent>{
public void onApplicationEvent(MyApplicationEvent event) {
/**
* 这里我们监听自己的事件,
* 当有此事件派发, 此方法就会触发
*/
Object source = event.getSource();
System.out.println("监听到事件:"+source);
}
}
复制代码
4) 运行容器,查看结果
可以看到,我们自己发布的事件,自己监听处理。发布几次就处理几次,是不是很酸爽呢?
系统如何派发自定义事件?【源码剖析】
上一章节我们演示了如何编写自定义事件,派发以及监听自定义事件。那么我们编写的事件,Spring是如何感知和帮我们派发事件的呢?在 <系统事件派发> 章节中,在容器初始化时,我们看到了这两个方法:如果忘了,可以返回到上面 “系统事件派发” 章节再看一次!
initApplicationEventMulticaster(); // 初始化事件派发器
复制代码
registerListeners();// 注册监听器
复制代码
初始化派发器
初始化派发器,先拿到beanFactory, 然后从本地容器中查找有没有创建好的派发器,如果没有则注册创建一个单例的 SimpleApplicationEventMulticaster
派发器放到容器中
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
复制代码
注册监听器
-
容器会拿到所有监听器,然后进行遍历添加到 派发器 的监听器List集合中
-
根据
ApplicationListener
类型拿到所有监听器的名称,存到 派发器的Set集合中
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
复制代码
派发事件
现在派发器,监听器都已经准备就绪,当我们调用发布事件方法时则触发派发方法
@Override
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}
复制代码
-
getApplicationEventMulticaster()
拿到事件派发器 -
multicastEvent()
发布事件, 观察源码,拿到事件对应的所有监听器,然后启动线程执行invokeListener
@Override
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener<?> listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
复制代码
invokeListener()
执行监听器,在这里我们清楚的看到,监听器执行了onApplicationEvent
方法,就是我们自定义监听器时,重写的方法。
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
listener.onApplicationEvent(event);
}
}
复制代码
至此源码分析就告一段落,想必你已经掌握了Spring事件监听的核心啦。
高阶用法之@EventListener
在实际开发中,Spring为了方便使用,只需要在想要监听事件的方法上,使用@EventListener
注解就能监听事件。
- 在想要监听的方法上标注注解
@Service
public class UserService {
@EventListener(classes = MyApplicationEvent.class)
public void listener(MyApplicationEvent event) {
Object source = event.getSource();
System.out.println("注解收到事件:"+source);
}
}
复制代码
2)监听方法的参数,就是你要传递的事件
3)启动容器,查看效果 。 非常酸爽!
- 再说原理
原理其实很简单,和我们之前分析的差不多,只是注入对象采用注解注入了。首先扫描对应的注解,然后事件派发时,调用该方法,然后传入事件。
注: 需要特别注意的是, 低版本的Spring是没有提供这个注解的。亲测Sping4.1.12版本是没有的。
结尾
SpringBoot事件监听机制 和 Spring几乎一致,所以各位小伙伴不必担心。掌握源码,则万变不离其宗。我是 尘心,如果这篇文章对你有帮助,可以点个赞鼓励一下。公众号(GitHub严选),技术经验与好用黑科技工具一起分享,进去先点福利清单,海量福利等着你。