Spring Bean 方法自動觸發的N種方式

有時候咱們須要在系統啓動後自動執行一些初始化動做,如加載自定義的資源,啓動業務相關定時器,或是在bean初始化後執行一些其餘的註冊動做。這裏面有個核心的要素就是方法的執行時機,總結來說spring裏面有兩大類的方法執行時機:java

  1. 生命週期回調觸發。
  2. spring 事件觸發。

經過生命週期回調觸發

spring官網中的有對生命週期回調的總結了初始化回調的三種方式:web

  1. Methods annotated with @PostConstruct
  2. afterPropertiesSet() as defined by the InitializingBean callback interface
  3. A custom configured init() method,一般用在xml配置中,在bean標籤中配置一個init-method實現

對應的註銷回調的三種方式:spring

  1. Methods annotated with @PreDestroy
  2. destroy() as defined by the DisposableBean callback interface
  3. A custom configured destroy() method,一般用在xml配置中,在bean標籤中配置一個destroy-method實現

這三種方式的執行時機如上述的順序。bash

PostConstruct的執行時機

afterPropertiesSet的執行時機

afterPropertiesSet是在AbstractAutowireCapableBeanFactoryinitializeBean中調用的,也就是bean的實例化流程中的初始化bean的時候,這個時候spring已經對bean完成了代理、依賴注入,開始執行一些初始化方法。app

具體執行的方法以下,注意啓動的invokeInitMethods,就是調用afterPropertiesSet的入口,這個方法的執行時機是後置處理器執行postProcessBeforeInitializationapplyBeanPostProcessorsAfterInitialization之間。ide

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}
複製代碼

再看看invokeInitMethods的具體實現post

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}
	}
複製代碼

經過spring事件來觸發

這種觸發方式是經過使用@EventListener註解來監聽spring發佈的事件,收到事件後執行某些業務操做。好比但願spring容器初始化完成後纔開始咱們的業務邏輯,以下是一個代碼示例:ui

@EventListener(classes = ContextRefreshedEvent.class)
public void enableSchedule() {  
    this.scheduleFlag = true;
}
複製代碼

spring官網的1.15.2 standard and custom events 中提到了spring的內建事件,詳細介紹以下:this

ontextRefreshedEvent Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, 「initialized」 means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such 「hot」 refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not.
ContextStartedEvent Published when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext interface. Here, 「started」 means that all Lifecycle beans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization).
ContextStoppedEvent Published when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext interface. Here, 「stopped」 means that all Lifecycle beans receive an explicit stop signal. A stopped context may be restarted through a start() call.
ContextClosedEvent Published when the ApplicationContext is being closed by using the close() method on the ConfigurableApplicationContext interface or via a JVM shutdown hook. Here, "closed" means that all singleton beans will be destroyed. Once the context is closed, it reaches its end of life and cannot be refreshed or restarted.
RequestHandledEvent A web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s DispatcherServlet.
ServletRequestHandledEvent A subclass of RequestHandledEvent that adds Servlet-specific context information.

spring應用上下文啓動事件——ontextRefreshedEvent

這個事件是在AbstractApplicationContext#finishRefresh方法中執行,執行到這裏的時候整個spring容器的初始化、實例化(單例)已經完成,具體代碼以下:spa

protected void finishRefresh() {
		......

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

		// Participate in LiveBeansView MBean, if active.
		LiveBeansView.registerApplicationContext(this);
	}
複製代碼

若是應用系統中,須要在容器初始化完成後(對應用bean有依賴)當即執行某些操做,能夠監聽這個事件,絕對不會出現依賴的bean未初始化的問題。

相關文章
相關標籤/搜索