#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.