ApplicationListener使用和发布原理


记录最近看视频学到的AppiicationListener的原理

ApplicationListener是用于容器初始化之后调用,如果想要在IOC容器创建完bean之后或者容器关闭的时候做一些操作,就可以用到这个监听器

1. 演示

ContextRefreshedEvent:容器创建完成(所有bean都创建)会发布这个事件
ContextClosedEvent:容器关闭也会发布事件

applicationContext.publishEvent():自己发布一个事件

@ComponentScan
public class MyApplicationListener implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("收到的事件:" + event);
    }
}
@Configuration
@Import({MyApplicationListener.class})
public class AddBeanConfig {

}

测试类:

  @Test
    public void test08(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class);
        applicationContext.publishEvent(new ApplicationEvent(new String("我自己发布的事件")) {
        });
        applicationContext.close();
       //收到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@136432db, started on Wed Aug 18 10:16:02 CST 2021]
        //收到的事件:com.test.AppTest$1[source=我自己发布的事件]
        //收到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@136432db, started on Wed Aug 18 10:16:02 CST 2021]
    }



2. 实现原理

2.1 ContextRefreshedEvent大概流程

1、我们打断点后查看方法调用栈,大概能看到用了什么方法
在这里插入图片描述

2、调用方法流程
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AddBeanConfig.class) ==> refresh() ==> finishRefresh() ==> publishEvent(new ContextRefreshedEvent(this)) ==> publishEvent(event, null) ==> getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) ⇒ invokeListener(listener, event) ⇒ doInvokeListener(listener, event) ⇒ listener.onApplicationEvent(event) ⇒ System.out.println(“收到的事件:” + event)


3、方法调用具体过程

(1)容器创建对象=>refresh()刷新对象
(2)finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
(3)publishEvent(new ContextRefreshedEvent(this)) :事件发布流程
      1. 获取事件的派发器:getApplicationEventMulticaster()
      2. 调用multicastEvent(applicationEvent, eventType)进行事件派发
        (1)先获取到所有的Listeners
        (2)获取Executor,如果Listener需要异步派发就使用Executor异步派发
        (3) 如果不需要就直接同步执行invokeListener(listener, event),然后调用doInvokeListener(listener, event)方法,接着调用listener.onApplicationEvent(event),就来到我们自己重写的onApplicationEvent方法里面了。第一次调用完就会发布ContextRefreshedEvent事件

@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}



2.2 applicationContext.publishEvent()大概流程

我们自己发送的事件,调用过程如下
在这里插入图片描述

方法调用过程和上面一模一样,只不过我们自己发布的事件监听是从publishEvent开始,省去了创建IOC容器和refresh()刷新容器的步骤


2.3 ContextClosedEvent大概流程

调用过程:
在这里插入图片描述
其实就是close ==> doClose() 和前面不一样,其他都是和ContextRefreshedEvent的流程一模一样。



2.4 细节一:如何获取派发器

getApplicationEventMulticaster()
(1)创建IOC容器,调用里面refresh方法
(2)refresh方法中有一个initApplicationEventMulticaster(),初始化事件派发器

protected void initApplicationEventMulticaster() {
		//获取bean工厂
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		//APPLICATION_EVENT_MULTICASTER_BEAN_NAME = applicationEventMulticaster、
		//判断bean工厂中是否含有id为applicationEventMulticaster的派发器
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
		    //如果有,就直接赋值
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
		    //如果没有,就new一个SimpleApplicationEventMulticaster,然后注册进bean工厂中
		    //注册完之后,下次再调用就会直接走上面的流程,直接从容器中拿
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

(3)上面的流程大概就是
     (1) 如果在容器中能找到事件派发器,就直接从容器中拿
     (2) 如果找不到,就创建一个SimpleApplicationEventMulticaster,注册进beanFactory中,下次再调用就直接从IOC容器中拿。



2.5 细节二:容器如何获取所有监听器

(1)创建IOC容器,调用里面refresh方法
(2)refresh方法中调用registerListeners()

protected void registerListeners() {
		//首先注册静态监听器
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		 //从容器中找到所有的ApplicationListener类型的监听器名字
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		//遍历for循环,把监听器名字加入派发器,以后我们要派发监听监听事件的时候就直接调用multicastEvent就行了
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// 首先派发earlyEvent,这个我也不知道什么意思,字面意思就是发布早期的应用事件
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}



2.6 总结流程

(1)容器创建对象,调用refresh方法

(2)首先获取事件派发器
     1、调用refresh的initApplicationEventMulticaster()方法,过程是2和3
     2、 如果在容器中能找到事件派发器,就直接从容器中拿
     3、如果找不到,就创建一个SimpleApplicationEventMulticaster,注册进beanFactory中,下次再调用就直接从IOC容器中拿。

(3)调用refresh的registerListeners()方法注册所有的监听器
     1、首先注册静态的监听器
     2、根据ApplicationListener.class获取所有的普通监听器的名字,加入到事件派发器中
     3、使用派发器发布早期的应用事件

(4)调用refresh中的finishRefresh方法,容器刷新完会发布ContextRefreshedEvent事件,finishRefresh的流程如下

(5)finishRefresh中的publishEvent(new ContextRefreshedEvent(this)) :事件发布流程
      1. 获取事件的派发器:getApplicationEventMulticaster()
      2. 调用multicastEvent(applicationEvent, eventType)进行事件派发
        (1)先获取到所有的Listeners
        (2)获取Executor,如果Listener需要异步派发就使用Executor异步派发
        (3) 如果不需要就直接同步执行invokeListener(listener, event),然后调用doInvokeListener(listener, event)方法,接着调用listener.onApplicationEvent(event),就来到我们自己重写的onApplicationEvent方法里面了。第一次调用完就会发布ContextRefreshedEvent事件

(6)ContextRefreshedEvent发布完成后,我们发布自己的监听事件
applicationContext.publishEvent(new ApplicationEvent(new String(“我自己发布的事件”)),直接进入publishEvent,流程和上面的一样

(7) 调用applicationContext.close()方法后,流程是close() -> doClose() ->publishEvent(new ContextClosedEvent(this)), 然后流程也和上面的一样了。






如有错误,欢迎指出

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值