Spring中的InitializingBean接口源碼解析
簡介
InitializingBean
InitializingBean接口為Bean初始化提供了一種方式。
實(shí)現(xiàn)InitializingBean接口的Bean,在BeanFactory設(shè)置其所有屬性后會調(diào)用其afterPropertiesSet()方法??梢栽赼fterPropertiesSet()方法中執(zhí)行自定義初始化、屬性檢查或強(qiáng)制校驗(yàn)等,若不滿足要求可以拋出異常以中斷Spring的加載流程。
InitializingBean應(yīng)用時(shí)有幾點(diǎn)需要注意:
① Bean必須實(shí)現(xiàn)InitializingBean接口。
② Bean的afterPropertiesSet不能使用@PostConstruct注釋。
init-method
init-method定義初始化方法為Bean初始化提供了另一種方式。
Bean聲明時(shí)配置init-method屬性,用于指定初始化方法。與InitializingBean方式類似,在BeanFactory設(shè)置其所有屬性后會調(diào)用其init-method指定的方法。可以在init-method方法中執(zhí)行自定義初始化、屬性檢查或強(qiáng)制校驗(yàn)等,若不滿足要求可以拋出異常以中斷Spring的加載流程。
init-method應(yīng)用時(shí)有幾個限制需要注意:
① init-method指定屬性不能為空。
② Bean不可以實(shí)現(xiàn)InitializingBean接口或Bean的init-method方法名不可以為afterPropertiesSet。
③ Bean的init-method方法不能使用@PostConstruct注釋。
演示示例
InitializingBean和init-method可以作用于同一個Bean,但是需要滿足上面所羅列的注意事項(xiàng),下面來使用一個簡單示例看一下。
1) 建Bean,實(shí)現(xiàn)InitializingBean接口,并額外填加一個方法用于init-method配置。
package com.arhorchin.securitit.initbean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; /** * @author Securitit. * @note Bean初始化測試. */ public class InitTestBean implements InitializingBean { /** * logger. */ private Logger logger = LoggerFactory.getLogger(InitTestBean.class); @Override public void afterPropertiesSet() throws Exception { logger.info("調(diào)用InitializingBean的afterPropertiesSet方法."); } public void initMethod() throws Exception { logger.info("調(diào)用init-method的initMethod方法."); } }
2) 在Spring的配置文件中增加Bean聲明,并指定init-method屬性。
<bean class="com.arhorchin.securitit.initbean.InitTestBean" init-method="initMethod"></bean>
3) 運(yùn)行程序查看效果,可以看到如下的輸出。
2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 調(diào)用InitializingBean的afterPropertiesSet方法.
2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 調(diào)用init-method的initMethod方法.
從結(jié)果可以看到,InitializingBean的afterPropertiesSet先于Bean的init-method指定的方法調(diào)用。
源碼解析
InitializingBean和init-method源碼基本集中在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類中。
1)initializeBean(...)方法
AbstractAutowireCapableBeanFactory的initializeBean(...)方法
initializeBean(...)方法中針對Bean進(jìn)行了幾個操作:
① 若Bean實(shí)現(xiàn)了Aware接口,則觸發(fā)方法調(diào)用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware。
② 調(diào)用注冊的BeanPostProcessor的postProcessBeforeInitialization(...)方法。
③ 調(diào)用初始化方法,包括InitializingBean的afterPropertiesSet()方法和Bean的init-method指定的方法。
④ 調(diào)用注冊的BeanPostProcessor的postProcessAfterInitialization(...)方法。
/** * 初始化給定的Bean實(shí)例,應(yīng)用工廠回調(diào)、init方法和Bean后處理程序. * 對于傳統(tǒng)定義的Bean,從createBean調(diào)用,對于現(xiàn)有的Bean實(shí)例從initializeBean調(diào)用. * @param beanName 工廠中的Bean名稱(用于調(diào)試). * @param bean 需要初始化新的Bean實(shí)例. * @param mbd 創(chuàng)建Bean時(shí)使用的Bean定義(如果給定現(xiàn)有的Bean實(shí)例,也可以是null). * @return 初始化的Bean實(shí)例(可能被包裝). */ protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { // 若Bean實(shí)現(xiàn)了Aware接口,則觸發(fā)方法調(diào)用. if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; // 在Bean初始化前處理BeanPostProcessor. if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // 進(jìn)行Bean初始化,包括如下兩種方式: // 1.調(diào)用InitializingBean.afterPropertiesSet()方法. // 2.調(diào)用Bean配置的init-method方法. try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } // 在Bean初始化后處理BeanPostProcessor. if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
2)實(shí)現(xiàn)Aware接口
若Bean實(shí)現(xiàn)了Aware接口,則觸發(fā)方法調(diào)用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware
/** * 若Bean實(shí)現(xiàn)了Aware接口,則觸發(fā)方法調(diào)用. */ private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { // 調(diào)用BeanNameAware.setBeanName(...)方法. if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } // 調(diào)用BeanClassLoaderAware.setBeanClassLoader(...)方法. if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } // 調(diào)用BeanFactoryAware.setBeanFactory(...). if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
3)InitializingBean的afterPropertiesSet方法調(diào)用
invokeInitMethods主要用于InitializingBean的afterPropertiesSet方法調(diào)用,從方法源碼中也可以看到,是先調(diào)用InitializingBean的afterPropertiesSet方法,然后再調(diào)用Bean的init-method指定的方法,查看代碼的注釋,可以看到相關(guān)的內(nèi)容,不做過多解析。
/** * 所有屬性設(shè)置完成后可選的Bean初始化. * Bean實(shí)現(xiàn)了InitializingBean接口或定義了init-method方法,則會進(jìn)行回調(diào)處理. * @param beanName 工廠中的Bean名稱(用于調(diào)試). * @param bean 需要初始化新的Bean實(shí)例. * @param mbd 創(chuàng)建Bean時(shí)使用的合并Bean定義(如果給定現(xiàn)有的Bean實(shí)例,也可以是null). */ protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // Bean是否實(shí)現(xiàn)了InitializingBean接口. boolean isInitializingBean = (bean instanceof InitializingBean); // 調(diào)用InitializingBean.afterPropertiesSet()方法.需要滿足條件: // 1.Bean實(shí)現(xiàn)了InitializingBean接口. // 2.Bean為空或afterPropertiesSet方法未被@PostConstruct注釋. if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } // 調(diào)用InitializingBean.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(); } } // 調(diào)用配置init-method方法處理. if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); // 1.init-method配置不能為空. // 2.Bean不能實(shí)現(xiàn)InitializingBean或init-method方法不是afterPropertiesSet. // 3.init-method方法未被@PostConstruct注釋. if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
4)invokeCustomInitMethod
invokeCustomInitMethod主要用于調(diào)用init-method指定的方法,調(diào)用方式僅是通過反射來調(diào)用,查看代碼的注釋,可以看到相關(guān)的內(nèi)容,不做過多解析。
/** * 在給定的Bean上調(diào)用指定的自定義init方法.由invokeInitMethods調(diào)用. * 可以在子類中重寫,以便使用參數(shù)自定義解析init方法. */ protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { // 取得Method實(shí)例. String initMethodName = mbd.getInitMethodName(); Assert.state(initMethodName != null, "No init method set"); final Method initMethod = (mbd.isNonPublicAccessAllowed() ? BeanUtils.findMethod(bean.getClass(), initMethodName) : ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName)); if (initMethod == null) { if (mbd.isEnforceInitMethod()) { throw new BeanDefinitionValidationException("Couldn't find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'"); } else { if (logger.isDebugEnabled()) { logger.debug("No default init method named '" + initMethodName + "' found on bean with name '" + beanName + "'"); } // Ignore non-existent default lifecycle methods. return; } } if (logger.isDebugEnabled()) { logger.debug("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'"); } // 調(diào)用Bean的init-method配置的方法. if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { ReflectionUtils.makeAccessible(initMethod); return null; }); try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> initMethod.invoke(bean), getAccessControlContext()); } catch (PrivilegedActionException pae) { InvocationTargetException ex = (InvocationTargetException) pae.getException(); throw ex.getTargetException(); } } else { try { ReflectionUtils.makeAccessible(initMethod); initMethod.invoke(bean); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
總結(jié)
InitializingBean是一個很神奇的接口,Spring框架中對InitializingBean的應(yīng)用很是頻繁,init-method同樣如此,一定要了解兩者之間的調(diào)用順序,才能在更細(xì)粒度控制Bean的初始化過程。
源碼解析基于spring-framework-5.0.5.RELEASE版本源碼。
若文中存在錯誤和不足,歡迎指正!
到此這篇關(guān)于Spring中的InitializingBean接口源碼解析的文章就介紹到這了,更多相關(guān)InitializingBean接口 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決SpringBoot多模塊發(fā)布時(shí)99%的問題
本文歸納了以下 8 個原則和發(fā)布時(shí)經(jīng)常出現(xiàn)的 4 個問題的解決方案,掌握了這些原則和解決方案,幾乎可以解決絕大數(shù)SpringBoot發(fā)布問題2019-07-07Scala數(shù)據(jù)庫連接池的簡單實(shí)現(xiàn)
本文主要介紹了Scala數(shù)據(jù)庫連接池的簡單實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02使用java編程從0到1實(shí)現(xiàn)一個簡單計(jì)算器
這篇文章主要介紹了使用java編程從0到1實(shí)現(xiàn)一個簡單計(jì)算器,文章中用代碼實(shí)例講解的很清晰,有感興趣的同學(xué)可以學(xué)習(xí)研究下2021-02-02詳解Java Bellman-Ford算法原理及實(shí)現(xiàn)
Bellman-Ford算法與Dijkstra算法類似,都是以松弛操作作為基礎(chǔ),Bellman-Ford算法是對所有邊都進(jìn)行松弛操作,本文將詳解Bellman-Ford算法原理及實(shí)現(xiàn),感興趣的可以了解一下2022-07-07Mybatis-Plus的條件構(gòu)造器QueryWrapper & UpdateWrapper示例詳解
Mybatis-Plus的條件構(gòu)造器QueryWrapper和UpdateWrapper為開發(fā)者提供了強(qiáng)大、靈活的條件構(gòu)建工具,能夠大大簡化數(shù)據(jù)庫操作的代碼,通過本文的介紹,讀者可以更加深入地理解這兩個條件構(gòu)造器的使用方法,并在實(shí)際項(xiàng)目中靈活應(yīng)用,感興趣的朋友跟隨小編一起看看吧2024-01-01Spring?Boot?詳細(xì)分析Conditional自動化配置注解
首先我們先了解一下@Conditional注解,@Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,需要注入的Bean滿足給定條件才可以注入到Spring?IOC容器中2022-07-07SpringBoot實(shí)現(xiàn)Read Through模式的操作過程
Read Through模式通常是指一種緩存策略,其中當(dāng)應(yīng)用程序嘗試讀取數(shù)據(jù)時(shí),緩存系統(tǒng)首先被檢查以查看數(shù)據(jù)是否已經(jīng)存在于緩存中,這篇文章主要介紹了SpringBoot實(shí)現(xiàn)Read Through模式,需要的朋友可以參考下2024-07-07