Spring中的InitializingBean接口源碼解析
簡介
InitializingBean
InitializingBean接口為Bean初始化提供了一種方式。
實現(xiàn)InitializingBean接口的Bean,在BeanFactory設置其所有屬性后會調用其afterPropertiesSet()方法??梢栽赼fterPropertiesSet()方法中執(zhí)行自定義初始化、屬性檢查或強制校驗等,若不滿足要求可以拋出異常以中斷Spring的加載流程。
InitializingBean應用時有幾點需要注意:
① Bean必須實現(xiàn)InitializingBean接口。
② Bean的afterPropertiesSet不能使用@PostConstruct注釋。
init-method
init-method定義初始化方法為Bean初始化提供了另一種方式。
Bean聲明時配置init-method屬性,用于指定初始化方法。與InitializingBean方式類似,在BeanFactory設置其所有屬性后會調用其init-method指定的方法。可以在init-method方法中執(zhí)行自定義初始化、屬性檢查或強制校驗等,若不滿足要求可以拋出異常以中斷Spring的加載流程。
init-method應用時有幾個限制需要注意:
① init-method指定屬性不能為空。
② Bean不可以實現(xiàn)InitializingBean接口或Bean的init-method方法名不可以為afterPropertiesSet。
③ Bean的init-method方法不能使用@PostConstruct注釋。
演示示例
InitializingBean和init-method可以作用于同一個Bean,但是需要滿足上面所羅列的注意事項,下面來使用一個簡單示例看一下。
1) 建Bean,實現(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("調用InitializingBean的afterPropertiesSet方法.");
}
public void initMethod() throws Exception {
logger.info("調用init-method的initMethod方法.");
}
}
2) 在Spring的配置文件中增加Bean聲明,并指定init-method屬性。
<bean class="com.arhorchin.securitit.initbean.InitTestBean" init-method="initMethod"></bean>
3) 運行程序查看效果,可以看到如下的輸出。
2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 調用InitializingBean的afterPropertiesSet方法.
2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 調用init-method的initMethod方法.
從結果可以看到,InitializingBean的afterPropertiesSet先于Bean的init-method指定的方法調用。
源碼解析
InitializingBean和init-method源碼基本集中在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類中。
1)initializeBean(...)方法
AbstractAutowireCapableBeanFactory的initializeBean(...)方法
initializeBean(...)方法中針對Bean進行了幾個操作:
① 若Bean實現(xiàn)了Aware接口,則觸發(fā)方法調用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware。
② 調用注冊的BeanPostProcessor的postProcessBeforeInitialization(...)方法。
③ 調用初始化方法,包括InitializingBean的afterPropertiesSet()方法和Bean的init-method指定的方法。
④ 調用注冊的BeanPostProcessor的postProcessAfterInitialization(...)方法。
/**
* 初始化給定的Bean實例,應用工廠回調、init方法和Bean后處理程序.
* 對于傳統(tǒng)定義的Bean,從createBean調用,對于現(xiàn)有的Bean實例從initializeBean調用.
* @param beanName 工廠中的Bean名稱(用于調試).
* @param bean 需要初始化新的Bean實例.
* @param mbd 創(chuàng)建Bean時使用的Bean定義(如果給定現(xiàn)有的Bean實例,也可以是null).
* @return 初始化的Bean實例(可能被包裝).
*/
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 若Bean實現(xiàn)了Aware接口,則觸發(fā)方法調用.
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);
}
// 進行Bean初始化,包括如下兩種方式:
// 1.調用InitializingBean.afterPropertiesSet()方法.
// 2.調用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)實現(xiàn)Aware接口
若Bean實現(xiàn)了Aware接口,則觸發(fā)方法調用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware
/**
* 若Bean實現(xiàn)了Aware接口,則觸發(fā)方法調用.
*/
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
// 調用BeanNameAware.setBeanName(...)方法.
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// 調用BeanClassLoaderAware.setBeanClassLoader(...)方法.
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// 調用BeanFactoryAware.setBeanFactory(...).
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
3)InitializingBean的afterPropertiesSet方法調用
invokeInitMethods主要用于InitializingBean的afterPropertiesSet方法調用,從方法源碼中也可以看到,是先調用InitializingBean的afterPropertiesSet方法,然后再調用Bean的init-method指定的方法,查看代碼的注釋,可以看到相關的內容,不做過多解析。
/**
* 所有屬性設置完成后可選的Bean初始化.
* Bean實現(xiàn)了InitializingBean接口或定義了init-method方法,則會進行回調處理.
* @param beanName 工廠中的Bean名稱(用于調試).
* @param bean 需要初始化新的Bean實例.
* @param mbd 創(chuàng)建Bean時使用的合并Bean定義(如果給定現(xiàn)有的Bean實例,也可以是null).
*/
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// Bean是否實現(xiàn)了InitializingBean接口.
boolean isInitializingBean = (bean instanceof InitializingBean);
// 調用InitializingBean.afterPropertiesSet()方法.需要滿足條件:
// 1.Bean實現(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 + "'");
}
// 調用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();
}
}
// 調用配置init-method方法處理.
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
// 1.init-method配置不能為空.
// 2.Bean不能實現(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主要用于調用init-method指定的方法,調用方式僅是通過反射來調用,查看代碼的注釋,可以看到相關的內容,不做過多解析。
/**
* 在給定的Bean上調用指定的自定義init方法.由invokeInitMethods調用.
* 可以在子類中重寫,以便使用參數(shù)自定義解析init方法.
*/
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
// 取得Method實例.
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 + "'");
}
// 調用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();
}
}
}總結
InitializingBean是一個很神奇的接口,Spring框架中對InitializingBean的應用很是頻繁,init-method同樣如此,一定要了解兩者之間的調用順序,才能在更細粒度控制Bean的初始化過程。
源碼解析基于spring-framework-5.0.5.RELEASE版本源碼。
若文中存在錯誤和不足,歡迎指正!
到此這篇關于Spring中的InitializingBean接口源碼解析的文章就介紹到這了,更多相關InitializingBean接口 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Boot 2.0 整合 Spring Security&
OAuth 2.0是一個行業(yè)標準的授權協(xié)議,它允許用戶在不將用戶名和密碼提供給第三方應用的情況下,授權第三方應用訪問用戶存儲在服務提供方的資源,本文給大家介紹Spring Boot 2.0 整合 Spring Security OAuth2的完整實現(xiàn)方案,感興趣的朋友一起看看吧2026-03-03
MyBatis-Plus條件構造器之condition參數(shù)的使用
這篇文章主要介紹了MyBatis-Plus條件構造器之condition參數(shù)的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12

