Spring中的InitializingBean和SmartInitializingSingleton的區(qū)別詳解
一,InitializingBean簡(jiǎn)介
InitializingBean的代碼:
package org.springframework.beans.factory; /** * Interface to be implemented by beans that need to react once all their * properties have been set by a BeanFactory: for example, to perform custom * initialization, or merely to check that all mandatory properties have been set. * * <p>An alternative to implementing InitializingBean is specifying a custom * init-method, for example in an XML bean definition. * For a list of all bean lifecycle methods, see the * {@link BeanFactory BeanFactory javadocs}. * * @author Rod Johnson * @see BeanNameAware * @see BeanFactoryAware * @see BeanFactory * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.context.ApplicationContextAware */ public interface InitializingBean { /** * Invoked by a BeanFactory after it has set all bean properties supplied * (and satisfied BeanFactoryAware and ApplicationContextAware). * <p>This method allows the bean instance to perform initialization only * possible when all bean properties have been set and to throw an * exception in the event of misconfiguration. * @throws Exception in the event of misconfiguration (such * as failure to set an essential property) or if initialization fails. */ void afterPropertiesSet() throws Exception; }
說明:
1,InitializingBean只有一個(gè)接口方法:afterPropertiesSet()
2,InitializingBean接口可以被bean實(shí)現(xiàn),并重寫afterPropertiesSet()方法。
3,功能:在BeanFactory初始化完這個(gè)bean,并且把bean的參數(shù)都注入成功后調(diào)用一次afterPropertiesSet()方法。
4,InitializingBean有個(gè)功能類似的替換方案:在XML配置文件中配置init-method。
二,SmartInitializingSingleton簡(jiǎn)介
SmartInitializingSingleton的代碼:
package org.springframework.beans.factory; /** * Callback interface triggered at the end of the singleton pre-instantiation phase * during {@link BeanFactory} bootstrap. This interface can be implemented by * singleton beans in order to perform some initialization after the regular * singleton instantiation algorithm, avoiding side effects with accidental early * initialization (e.g. from {@link ListableBeanFactory#getBeansOfType} calls). * In that sense, it is an alternative to {@link InitializingBean} which gets * triggered right at the end of a bean's local construction phase. * * <p>This callback variant is somewhat similar to * {@link org.springframework.context.event.ContextRefreshedEvent} but doesn't * require an implementation of {@link org.springframework.context.ApplicationListener}, * with no need to filter context references across a context hierarchy etc. * It also implies a more minimal dependency on just the {@code beans} package * and is being honored by standalone {@link ListableBeanFactory} implementations, * not just in an {@link org.springframework.context.ApplicationContext} environment. * * <p><b>NOTE:</b> If you intend to start/manage asynchronous tasks, preferably * implement {@link org.springframework.context.Lifecycle} instead which offers * a richer model for runtime management and allows for phased startup/shutdown. * * @author Juergen Hoeller * @since 4.1 * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons() */ 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(); }
說明:
1,SmartInitializingSingleton中只有一個(gè)接口方法:afterSingletonsInstantiated()
2,從注釋中可以看到,SmartInitializingSingleton接口可以被單例的bean實(shí)現(xiàn),并重寫afterSingletonsInstantiated()方法。
3,bean的afterSingletonsInstantiated()方法是在所有單例bean都初始化完成后才會(huì)調(diào)用的。
4,此接口可以解決一些因?yàn)閎ean初始化太早而出現(xiàn)的錯(cuò)誤和問題。
5,此接口是從Spring4.1版本開始使用的。
6,此接口可以作為InitializingBean接口的一種替代方案。
三,InitializingBean和SmartInitializingSingleton的區(qū)別
1,SmartInitializingSingleton只作用于單例bean,InitializingBean無(wú)此要求。但他們都不能用于懶加載的bean。
2,SmartInitializingSingleton是在所有單例Bean都初始化完成后調(diào)用的,InitializingBean是每個(gè)bean初始化完成后就會(huì)調(diào)用。
四,從源碼觀察InitializingBean和SmartInitializingSingleton調(diào)用時(shí)機(jī)的區(qū)別
在Spring進(jìn)行依賴注入的過程中,代碼會(huì)來到DefaultListableBeanFactory類的preInstantiateSingletons()方法:
@Override public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("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. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; 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 (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
方法很明確的分成了兩部分。
第一部分是從beanDefinitionNames列表中獲得所有要注入的bean,然后調(diào)用getBean()方法完成對(duì)bean的注入,InitializingBean. afterPropertiesSet()就是在getBean()方法中調(diào)用的。
第二部分再一次從beanDefinitionNames列表中獲得所有要注入的bean,并且調(diào)用他們的SmartInitializingSingleton. afterSingletonsInstantiated()方法。
以上就是這兩個(gè)接口生效位置的不同。
下面還可以看一下具體調(diào)用InitializingBean. afterPropertiesSet()方法的代碼,在上面第一部分調(diào)用getBean()方法的代碼中,經(jīng)過多層調(diào)用,代碼會(huì)來到AbstractAutowireCapableBeanFactory的invokeInitMethods()方法:
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 (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } 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(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
這個(gè)方法的代碼也是分成了明顯的兩部分,第一部分代碼就是調(diào)用bean的afterPropertiesSet()方法,第二部分調(diào)用了bean的initMethod,這個(gè)initMethod是可以在配置文件中配置的。
通過這個(gè)方法可以看到,afterPropertiesSet()方法是在initMethod之前調(diào)用的,而且看起來如果afterPropertiesSet()方法如果拋出了異常,那么initMethod方法也不會(huì)調(diào)用了(沒有安全管理器的話)。
到此這篇關(guān)于Spring中的InitializingBean和SmartInitializingSingleton的區(qū)別詳解的文章就介紹到這了,更多相關(guān)InitializingBean和SmartInitializingSingleton區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL8.0無(wú)法遠(yuǎn)程連接訪問的解決方法
本文主要介紹了MySQL8.0無(wú)法遠(yuǎn)程連接訪問的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01徹底搞懂?dāng)?shù)據(jù)庫(kù)操作truncate delete drop關(guān)鍵詞的區(qū)別
這篇文章主要為大家介紹了數(shù)據(jù)庫(kù)操作truncate delete drop關(guān)鍵詞的區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09MySQL數(shù)據(jù)庫(kù) Load Data 多種用法
這篇文章主要介紹MySQL數(shù)據(jù)庫(kù)中 Load Data 數(shù)據(jù)的幾種種用法,需要的朋友可以參考下面文章的解說2021-08-08Mysql遷移到TiDB雙寫數(shù)據(jù)庫(kù)兜底方案詳解
這篇文章主要為大家介紹了Mysql遷移到TiDB雙寫數(shù)據(jù)庫(kù)兜底方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01mysql導(dǎo)入sql文件命令和mysql遠(yuǎn)程登陸使用詳解
這篇文章主要介紹了mysql導(dǎo)入sql文件命令和mysql遠(yuǎn)程登陸使用詳解,需要的朋友可以參考下2014-04-04MySQL數(shù)據(jù)庫(kù)數(shù)據(jù)塊大小及配置方法
MySQL作為一種流行的關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng),在處理大規(guī)模數(shù)據(jù)存儲(chǔ)和查詢時(shí),數(shù)據(jù)塊(data block)大小是一個(gè)至關(guān)重要的因素,本文將詳細(xì)探討MySQL數(shù)據(jù)庫(kù)的數(shù)據(jù)塊大小,結(jié)合實(shí)際例子說明其重要性和配置方法,感興趣的朋友跟隨小編一起看看吧2024-05-05mysql數(shù)據(jù)庫(kù)的分區(qū)表示例代碼
這篇文章主要介紹了mysql數(shù)據(jù)庫(kù)的分區(qū)表的相關(guān)資料,文章介紹了兩種創(chuàng)建SQL表分區(qū)的方法,分別是手動(dòng)創(chuàng)建和使用MySQL的定時(shí)事件來自動(dòng)創(chuàng)建分區(qū),手動(dòng)創(chuàng)建分區(qū)時(shí),需要在代碼中判斷分區(qū)并新增,可能會(huì)引入一些問題,需要的朋友可以參考下2024-11-11MySQL 5.6主從報(bào)錯(cuò)的實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于MySQL 5.6主從報(bào)錯(cuò)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03