当前位置:首页 » 《随便一记》 » 正文

Spring监听器---ApplicationListener_兢兢业业的子牙的博客

29 人参与  2021年11月30日 15:03  分类 : 《随便一记》  评论

点击全文阅读


说到事件监听,想到的肯定是观察者模式。但是这儿主要说下spring中的监听是怎么样的流程。

这就不得不说到spring容器的refresh方法,容器启动过程中,listener相关的主要是这个三个方法:initApplicationEventMulticaster方法初始化事件多播器,后续的事件发布都是由多播器来发布的;registerListeners注册监听器到前面初始化好的多播器上面去;

finishRefresh容器启动完成最后刷新,发布ContextRefreshedEvent事件。
@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);

				// 最后容器刷新 发布刷新事件(springCloud也是从这里启动的)Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					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;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

1.初始化多播器

获取bean工厂对象ConfigurableListableBeanFactory,判断容器中是否有applicationEventMulticaster多播器,如果没有则创建一个一个简单事件多播器SimpleApplicationEventMulticaster并注册到容器中,后续使用

/**
	 * Initialize the ApplicationEventMulticaster.
	 * Uses SimpleApplicationEventMulticaster if none defined in the context.
	 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
	 */
	public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
	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.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			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() + "]");
			}
		}
	}

2.注册监听器到多播器上并发布早期事件

首先获取容器中已有的监听器(成品对象,从第一张图中可以看到我们自己的组件对象在registerListeners方法调用的时候 还没有初始化,是在下面的finishBeanFactoryInitialization方法中才进行初始化的),注册到多播器;然后获取bean定义中的监听器,也就是我们自己定义的监听器;同样也注册到多播器上去;最后如果有早期事件就去发布早期事件(multicastEvent方法),这些事件只能由已经实例化的监听器监听,我们自己的监听器初始化是在finishBeanFactoryInitialization方法中。

 

    /**
	 * Add beans that implement ApplicationListener as listeners.
	 * Doesn't affect other listeners, which can be added without being beans.
	 */
	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 listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// Publish early application events now that we finally have a multicaster...
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

发布事件:multicastEvent方法----->invokeListener方法---->doInvokeListener方法调用监听器的onApplicationEvent

	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@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);
			}
		}
	}

可以看到这里支持异步发送,但是从上面我们初始化简单多播器的时候,executer对象并没有赋值,因此始终是同步发布。如果我们想实现异步发布事件,那么就要让上面初始化多播器的逻辑走进第一个分支。我们可以在容器中自己继承SimpleApplicationEventMulticaster,并初始化一个线程池,然后将其注册到容器中,bean的名字必须使用“applicationEventMulticaster”,因为此时容器还没有创建真正的对象,只有这个名字的bean定义才会马上去创建对象。这样就可以实现异步发布事件了。

3.执行finishRefresh方法发布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() {
		// Clear context-level resource caches (such as ASM metadata from scanning).
		clearResourceCaches();

		// Initialize lifecycle processor for this context.
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// Publish the final event.
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		LiveBeansView.registerApplicationContext(this);
	}

监听器的流程完了,我们现在来看下使用

4.实现一个自己的监听器

4.1 监听器


@Component
public class MyListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("收到事件:" + event.toString());
    }
}

4.2 新建一个自己的事件:


public class MyApplicationEvent extends ApplicationEvent {
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public MyApplicationEvent(Object source) {
        super(source);
    }
}

4.3 配置类:指定要扫描的包


@Configuration
@ComponentScan(basePackages = "com.nijunyang.spring.*")
public class MainConfig {
}

4.4 测试代码:在容器创建完之后发布自己的事件。


public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        context.publishEvent(new MyApplicationEvent("想涨工资"));
    }

}

执行代码会发现我们的监听器会监听到两个事件,因为我们监听器监听的事件是ApplicationEvent,上面说到容器启动的时候,最后会执行finishRefresh方法发布ContextRefreshedEvent事件,容器启动完成之后,我们自己手动发布了一个我们自己的事件,因此会监听到两个事件。

 

4.5 修改我们的监听器,只监听我们自己的事件:


@Component
public class MyListener implements ApplicationListener<MyApplicationEvent> {
    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("收到事件:" + event.toString());
    }
}

 再次执行代码,发现现在就只能监听我们自己的事件了

 

通过spring的监听器,我们不仅可以实现自己相关的业务,还可以通过这个机制将我们自己的组件和spring进行整合,比如阿里的nacos就是通过ApplicationListener与spring整合的;

springboot和spring中的一些事件:

  • ContextClosedEvent:容器关闭的时候,我们可以监听这个事件在容器关闭的时候去清理一些缓存(比如redis)的数据
  • ApplicationFailedEvent:该事件为spring boot启动失败时的操作
  • ApplicationPreparedEvent:上下文context准备时触发
  • ApplicationReadyEvent:上下文已经准备完毕的时候触发,做权限认证的时候。在这个时候就可以去初始化一些权限数据。或者预备其他数据
  • ApplicationEnvironmentPreparedEvent:环境事先准备

点击全文阅读


本文链接:http://m.zhangshiyu.com/post/31455.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1