最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中的InitializingBean接口源碼解析

 更新時間:2024年02月14日 09:09:13   作者:securitit  
這篇文章主要介紹了Spring中的InitializingBean接口源碼解析,InitializingBean接口為Bean初始化提供了一種方式,實現(xiàn)InitializingBean接口的Bean,在BeanFactory設置其所有屬性后會調用其afterPropertiesSet()方法,需要的朋友可以參考下

簡介

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ù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java 8 Stream filter流式過濾器詳解

    Java 8 Stream filter流式過濾器詳解

    本文介紹了Java 8的Stream API中的filter方法,展示了如何使用lambda表達式根據(jù)條件過濾流式數(shù)據(jù),通過實際代碼示例,展示了filter方法的高效性以及如何結合findAny和orElse方法處理更復雜的情況,適合Java新手和追求代碼優(yōu)雅的開發(fā)者閱讀,感興趣的朋友一起看看吧
    2025-02-02
  • java實現(xiàn)2048小游戲

    java實現(xiàn)2048小游戲

    這篇文章主要為大家詳細介紹了java實現(xiàn)2048小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • Spring Boot 2.0 整合 Spring Security OAuth2的完整實現(xiàn)方案

    Spring Boot 2.0 整合 Spring Security&

    OAuth 2.0是一個行業(yè)標準的授權協(xié)議,它允許用戶在不將用戶名和密碼提供給第三方應用的情況下,授權第三方應用訪問用戶存儲在服務提供方的資源,本文給大家介紹Spring Boot 2.0 整合 Spring Security OAuth2的完整實現(xiàn)方案,感興趣的朋友一起看看吧
    2026-03-03
  • Java ArrayList遍歷修改代碼實例解析

    Java ArrayList遍歷修改代碼實例解析

    這篇文章主要介紹了Java ArrayList遍歷修改代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • java的線程池框架及線程池的原理

    java的線程池框架及線程池的原理

    這篇文章主要介紹了java的線程池框架及線程池的原理的相關資料,需要的朋友可以參考下
    2017-03-03
  • Feign遠程調用參數(shù)里面內容丟失的解決方案

    Feign遠程調用參數(shù)里面內容丟失的解決方案

    這篇文章主要介紹了Feign遠程調用參數(shù)里面內容丟失的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • MyBatis-Plus條件構造器之condition參數(shù)的使用

    MyBatis-Plus條件構造器之condition參數(shù)的使用

    這篇文章主要介紹了MyBatis-Plus條件構造器之condition參數(shù)的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • Nett中的心跳機制與斷線重連詳解

    Nett中的心跳機制與斷線重連詳解

    這篇文章主要介紹了Nett中的心跳機制與斷線重連詳解,我們以客戶端發(fā)送心跳為例,平時我們的心跳實現(xiàn)方式可能是搞個定時器,定時發(fā)送是吧,但是在Netty中卻不一樣,心跳被稱為空閑檢測,需要的朋友可以參考下
    2023-12-12
  • java對象與json對象間的相互轉換的方法

    java對象與json對象間的相互轉換的方法

    本篇文章主要介紹了java對象與json對象間的相互轉換的方法,詳細介紹了json字符串和java對象相互轉換,有興趣的可以了解一下
    2017-01-01
  • java實現(xiàn)String字符串處理各種類型轉換

    java實現(xiàn)String字符串處理各種類型轉換

    在日常的程序開發(fā)中,經(jīng)常會涉及到不同類型之間的轉換,本文主要介紹了String字符串處理各種類型轉換,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10

最新評論

延边| 石景山区| 香格里拉县| 景谷| 加查县| 安多县| 自治县| 吉安县| 安西县| 惠州市| 房产| 江达县| 灵寿县| 松滋市| 宁国市| 墨脱县| 榆中县| 榆社县| 盘锦市| 西藏| 班玛县| 淮安市| 洛扎县| 大丰市| 中西区| 广州市| 什邡市| 双鸭山市| 新河县| 盐城市| 霍州市| 基隆市| 英德市| 胶州市| 峡江县| 深泽县| 铜山县| 深州市| 营山县| 鸡东县| 拉萨市|