前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring容器初始化完成的回调方法

Spring容器初始化完成的回调方法

原创
作者头像
冰枫
修改2018-04-25 14:35:14
3.3K2
修改2018-04-25 14:35:14
举报
文章被收录于专栏:冰枫冰枫

我们可能经常会碰到一些奇奇怪怪的需求,比如在IOC容器初始化完成前实例化一些bean,bean的初始化回调等等等。今天来讲一下如何实现Spring IOC容器如何在完成初始化后回调某个方法。

这是Spring IOC容器的初始化方法,可以看到,它完成了,初始化的准备,beanFactory的初始化,beanDefinition的定位,加载,注册,beanFactory的后处理等。而我们要的是在初始化完成后回调某个方法,那如何实现呢?

代码语言:javascript
复制
@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;
			}
		}
	}

初始化完成,那么肯定是在最后一个方法里咯,我们来看finishRefresh()方法:

第三个方法是将将初始化完成的事件推送给listener,那么就意味着,它会调用listener的某个方法,这不是正好满足我们的需求吗?

代码语言:javascript
复制
protected void finishRefresh() {
		// 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);
	}
代码语言:javascript
复制
@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);
		}
	}

可以发现,它会先获取listener的集合,然后依次调用它们的onApplicationEvent(event)方法,如果它有存在父容器,会递归调用publishEvent(event)方法来通知所有容器中的listener,Spring提供了这个ApplicationListener接口来供我们进行功能的扩展。

代码语言:javascript
复制
@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);
			}
		}
	}
代码语言:javascript
复制
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);
		}
	}

那么我们实现这个接口

代码语言:javascript
复制
@Component
public class Test implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("我在bean初始化完之后执行 "+event.toString());
    }
}

输入如下,其是在IOC容器初始化完毕后进行通知listener来进行某些功能的实现的。

代码语言:javascript
复制
我在bean初始化完之后执行 org.springframework.context.event.ContextRefreshedEvent[source=cn.blingfeng.MyClassPathXmlApplicationContext@246b179d: startup date [Wed Apr 25 13:47:46 CST 2018]; root of context hierarchy]

那么问题来了,它是在什么时候将这些listener注册进容器的呢?

代码语言:javascript
复制
当然是上面的 registerListeners(); 这个方法啦

这里实现了将实现了ApplicationListener接口的Bean放入Set集合,那么就可以轻松实现这批listener的方法回调

代码语言:javascript
复制
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);
		}
	}

Spring中提供了很多IOC/Bean不同生命周期的方法回调,开放性很高,扩展性很强,我们可以根据自己的业务场景,实现不同的需求。

ps:本博近期会一直更新Spring源码系列内容

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档