Spring source code - core interface SmartInitializingSingleton&InitializingBean

Posted by shavas on Sat, 15 Jan 2022 19:32:47 +0100

#Spring source code - core interface smartinitializingsingleton & initializingbean

Spring version: Spring 5.3.13-release

#1. Core interface smartinitializingsingsingleton

Smartinitializingsingsingleton interface:

public interface SmartInitializingSingleton {

	/**
	 * Invoked right at the end of the singleton pre-instantiation phase,
	 * with a guarantee that all regular singleton beans have been created
	 * already. {@link ListableBeanFactory#getBeansOfType} calls within
	 * this method won't trigger accidental side effects during bootstrap.
	 * <p><b>NOTE:</b> This callback won't be triggered for singleton beans
	 * lazily initialized on demand after {@link BeanFactory} bootstrap,
	 * and not for any other bean scope either. Carefully use it for beans
	 * with the intended bootstrap semantics only.
	 */
	void afterSingletonsInstantiated();

}

The afterSingletonsInstantiated() method in the smartinitializingsingsingleton interface will call back after all non lazy single instance beans are initialized. Unexpected side effects of early initialization can be avoided. SmartInitializingSingleton can be regarded as a substitute for InitializingBean interface after all Bean initialization is completed. You can get some information about smartinitializingsingsingleton interface from the comment document:

  • The smartinitializingsingsingleton interface is triggered only for non lazy single instance beans.
  • The afterSingletonsInstantiated() method calls back after all non lazy single instance beans are initialized.
  • The SmartInitializingSingleton interface can be used as an alternative to the InitializingBean interface.

#2. Core interface InitializingBean

InitializingBean interface:

public interface InitializingBean {

	/**
	 * Invoked by the containing {@code BeanFactory} after it has set all bean properties
	 * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
	 * <p>This method allows the bean instance to perform validation of its overall
	 * configuration and final initialization when all bean properties have been set.
	 * @throws Exception in the event of misconfiguration (such as failure to set an
	 * essential property) or if initialization fails for any other reason
	 */
	void afterPropertiesSet() throws Exception;

}

The official explanation given by Spring for the role of the InitializingBean interface is: a Bean that implements the InitializingBean interface. After the Bean is instantiated and all properties in the Bean are injected by BeanFactory, check the settings of all mandatory properties. Some information about InitializingBean interface can be obtained from the annotation document:

  • The afterpropertieset() method is activated after the BeanFactory fills in the properties for the Bean.
  • The afterpropertieset () method allows the Bean to inject properties into the BeanFactory and verify the overall configuration after the final initialization.
  • The afterpropertieset () method can throw an exception when the Bean is misconfigured (such as failing to set a basic property) or fails to initialize for any other reason.
  • The InitializingBean interface has an alternative with similar functions, that is, configure the init method initialization method when declaring the Bean in the XML configuration file.

#3. The difference between smartinitializingsingleton and initializingbean

  • The SmartInitializingSingleton interface can only act on non lazy single instance beans, which is not required for the InitializingBean interface.
  • The smartinitializingsingsingleton interface is an activation callback after the initialization of all non lazy single instances, and the InitializingBean interface is an activation callback after the initialization of each Bean instance.

#4. Activation time of smartinitializingsingleton & initializingbean

#4.1 activation of smartinitializingsingsingleton

The activation of SmartInitializingSingleton is completed in the finishBeanFactoryInitialization() method in the Spring container refresh. The specific code is activated in the DefaultListableBeanFactory#preInstantiateSingletons() method.

DefaultListableBeanFactory#preInstantiateSingletons() activation code, omitting:

	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// Create a copy of beanDefinitionNames for subsequent traversal to allow the registration of new BeanDefinition using methods such as init
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// Trigger the initialization of all non delayed loading singleton beans and traverse the BeanName collection object
		for (String beanName : beanNames) {

			// The public abstract class of Bean definition information is AbstractBeanDefinition. When Spring parses Bean definition information, ordinary beans instantiate GenericBeanDefinition
			// The Spring context includes instantiating all beans. The AbstractBeanDefinition used is RootBeanDefinition
			// The getbeanrootdefinition method is converted to a non localbeanrootdefinition method for subsequent operations
			// Note that if there is a parent BeanDefinition in the current BeanDefinition, a RootBeanDefinition will be generated based on the parent BeanDefinition, and then the relevant properties calling OverrideFrom child BeanDefinition will be overwritten
			// Merge parent class BeanDefinition
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

			// Conditional judgment. If the non abstract class & & singleton & & non lazy loading, the singleton object is created and initialized by calling the getBean(beanName) method
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				// Judge whether the BeanFactory interface is implemented. If the BeanFactory interface is implemented, judge whether to initialize immediately
				// Judge whether the Bean is initialized immediately, according to whether the Bean implements the SmartFactoryBean interface, rewrite the internal method isEagerInit and return true
				if (isFactoryBean(beanName)) {
					// If it is a FactoryBean, obtain the FactoryBean object in the IOC container according to & + beanname
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					// If the Bean obtained from '&' + beanname is an instance of FactoryBean type
					if (bean instanceof FactoryBean) {
						// Cast Bean to factorybean <? >
						FactoryBean<?> factory = (FactoryBean<?>) bean;
						// Determine whether the FactoryBean wants to initialize immediately
						boolean isEagerInit;
						// Judge whether the Bean implements the SmartFactoryBean interface. If it implements SmartFactoryBean, call isEagerInit
						// Method to determine whether you want to initialize urgently
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(
									(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						// If you want to initialize urgently, call the getBean method to get the Bean instance object through beanName
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					// If the Bean corresponding to beanName is not a FactoryBean but an ordinary Bean, call the getBean method to obtain the Bean instance object through beanName
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		// Traverse beanNames and trigger all callbacks initialized after smartinitializingsingsingleton
		for (String beanName : beanNames) {
			// Get the singleton Bean instance object corresponding to beanName
			Object singletonInstance = getSingleton(beanName);
			// Judge whether the obtained singletonInstance singleton Bean object implements the smartinitializingsingsingleton interface
			if (singletonInstance instanceof SmartInitializingSingleton) {
				StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
						.tag("beanName", beanName);
				// Convert the obtained singletonInstance type to the smartinitializingsingsingleton interface
				SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				// Trigger the afterSingletonsInstantiated() method of the smartinitializingsingsingleton implementation class
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
				smartInitialize.end();
			}
		}
	}

The above source code is Spring's activation of smartinitializingsingsingleton #aftersingletonsinstantiated(). Two things were done:

  • 1. Instantiate Bean initialization using the getBean() method.
  • 2. Activate smartinitializingsingsingleton #aftersingletonsinstantiated().
  • 3. Spring activates smartinitializingsingsingleton after all non lazy single instance beans are initialized.

#4.1 activation of InitializingBean

The InitializingBean interface is activated when Spring performs getBean(). The specific code is activated in the abstractautowirecapablebeanfactory #invokeinitialmethods() method:

	protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {
		// Determine whether the Bean is an InitializingBean instance type
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			// If the Bean is an InitializingBean instance type, call the afterPropertiesSet method of InitializingBean directly
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						// Activate the afterPropertiesSet method
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				// Activate the afterPropertiesSet method
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		// MergedBeanDefinition is not null & & bean is not null
		if (mbd != null && bean.getClass() != NullBean.class) {
			// Gets the name of the custom init method method
			String initMethodName = mbd.getInitMethodName();
			// Judge whether the init method method is specified. If the init method method is specified, call the specified init method
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				// Call init method through reflection
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

The above source code is Spring's activation of InitializingBean. Two things were done:

  • 1. If the Bean instance is an InitializingBean type instance, InitializingBean#afterPropertiesSet() will be activated directly.
  • 2. If the Bean instance specifies the init method, activate the init method specified by the Bean through reflection.
  • 3. If Spring activates an instance of InitializingBean type abnormally, the init method method specified by the Bean will not be called.
  • 4. Spring activates InitializingBean after Bean initialization is completed.

GitHub source address: https://github.com/kapbc/kapcb-spring-source/tree/master/Spring-Framework-v5.3.13

Note: This article is the author's notes on learning the Spring source code. In view of my limited technology, some mistakes are inevitable in the article. Thank you for your criticism and correction.

Topics: Java Spring Spring Boot