欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring监听器---ApplicationListener

程序员文章站 2022-05-01 11:34:33
...

说到事件监听,想到的肯定是观察者模式。但是这儿主要说下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事件,容器启动完成之后,我们自己手动发布了一个我们自己的事件,因此会监听到两个事件。Spring监听器---ApplicationListener

 

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


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

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

 

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

springboot和spring中的一些事件:

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