欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java工廠實(shí)例BeanFactoryPostProcessor和BeanPostProcessor區(qū)別分析

 更新時(shí)間:2023年07月18日 09:15:26   作者:江南一點(diǎn)雨  
這篇文章主要為大家介紹了BeanFactoryPostProcessor和BeanPostProcessor區(qū)別示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

研究 Spring 源碼的小伙伴可能會(huì)發(fā)現(xiàn),Spring 源碼中有很多名稱特別相近的 Bean,我就不挨個(gè)舉例了,今天我是想和小伙伴們聊一聊 Spring 中 BeanFactoryPostProcessor 和 BeanPostProcessor 兩個(gè)處理器的區(qū)別。

我將從以下幾個(gè)方面來和小伙伴們分享。

1. 區(qū)別

這兩個(gè)接口說白了都是 Spring 在初始化 Bean 時(shí)對(duì)外暴露的擴(kuò)展點(diǎn),因?yàn)?Spring 框架提供的功能不一定能夠滿足我們所有的需求,有的時(shí)候我們需要對(duì)其進(jìn)行擴(kuò)展,那么這兩個(gè)接口就是用來做擴(kuò)展功能的。

其實(shí)不用看源碼,單純從字面上看,大家應(yīng)該也能理解個(gè)差不多:

BeanFactoryPostProcessor 是針對(duì) BeanFactory 的處理器。

BeanPostProcessor 則是針對(duì) Bean 的處理器。

我們先來看下 BeanFactoryPostProcessor 接口:

@FunctionalInterface
public?interface?BeanFactoryPostProcessor?{
?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?throws?BeansException;
}

可以看到,這里的參數(shù)實(shí)際上就是一個(gè) BeanFactory,在這個(gè)地方,我們可以對(duì) BeanFactory 進(jìn)行修改,重新進(jìn)行定制。例如可以修改一個(gè) Bean 的作用域,可以修改屬性值等。

再來看看 BeanPostProcessor 接口:

public?interface?BeanPostProcessor?{
?@Nullable
?default?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{
??return?bean;
?}
?@Nullable
?default?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{
??return?bean;
?}
}

可以看到,這里是兩個(gè)方法,這兩個(gè)方法都有一個(gè) bean 對(duì)象,說明這里被觸發(fā)的時(shí)候,Spring 已經(jīng)將 Bean 初始化了,然后才會(huì)觸發(fā)這里的兩個(gè)方法,我們可以在這里對(duì)已經(jīng)到手的 Bean 進(jìn)行額外的處理。其中:

  • postProcessBeforeInitialization:這個(gè)方法在 InitializingBean#afterPropertiesSet 和 init-method 方法之前被觸發(fā)。
  • postProcessAfterInitialization:這個(gè)方法在 InitializingBean#afterPropertiesSet 和 init-method 方法之后被觸發(fā)。

總結(jié)一下:

在 Spring 中,BeanFactoryPostProcessor 和 BeanPostProcessor 是兩個(gè)不同的接口,它們?cè)?Bean 的生命周期中扮演不同的角色。

  • BeanFactoryPostProcessor 接口用于在 Bean 工廠實(shí)例化 Bean 之前對(duì) Bean 的定義進(jìn)行修改。它可以讀取和修改 Bean 的定義元數(shù)據(jù),例如修改 Bean 的屬性值、添加額外的配置信息等。BeanFactoryPostProcessor 在 Bean 實(shí)例化之前執(zhí)行,用于對(duì) Bean 的定義進(jìn)行預(yù)處理。
  • BeanPostProcessor 接口用于在 Bean 實(shí)例化后對(duì) Bean 進(jìn)行增強(qiáng)或修改。它可以在 Bean 的初始化過程中對(duì) Bean 進(jìn)行后處理,例如對(duì) Bean 進(jìn)行代理、添加額外的功能等。BeanPostProcessor 在 Bean 實(shí)例化完成后執(zhí)行,用于對(duì) Bean 實(shí)例進(jìn)行后處理。

一言以蔽之,BeanFactoryPostProcessor 主要用于修改 Bean 的定義,而 BeanPostProcessor 主要用于增強(qiáng)或修改 Bean 的實(shí)例。

2. 代碼實(shí)踐

2.1 BeanFactoryPostProcessor

BeanFactoryPostProcessor 在 Spring 容器中有一個(gè)非常典型的應(yīng)用。

當(dāng)我們?cè)?Spring 容器中配置數(shù)據(jù)源的時(shí)候,一般都是按照下面這樣的方式進(jìn)行配置的。

首先創(chuàng)建 db.properties,將數(shù)據(jù)源各種信息寫入進(jìn)去:

db.username=root
db.password=123
db.url=jdbc:mysql:///db01?serverTimezone=Asia/Shanghai

然后在 Spring 的配置文件中,首先把這個(gè)配置文件加載進(jìn)來,然后就可以在 Spring Bean 中去使用對(duì)應(yīng)的值了,如下:

<context:property-placeholder?location="classpath:db.properties"/>
<bean?class="com.alibaba.druid.pool.DruidDataSource"?id="dataSource">
????<property?name="username"?value="${db.username}"/>
????<property?name="password"?value="${db.password}"/>
????<property?name="url"?value="${db.url}"/>
</bean>

但是大家知道,對(duì)于 DruidDataSource 來說,毫無疑問,它要的是具體的 username、password 以及 url,而上面的配置很明顯中間還有一個(gè)轉(zhuǎn)換的過程,即把 ${db.username}${db.password} 以及 ${db.url} 轉(zhuǎn)為具體對(duì)應(yīng)的值。那么這個(gè)轉(zhuǎn)換是怎么實(shí)現(xiàn)的呢?

這就得分析 <context:property-placeholder location="classpath:db.properties"/> 配置了,小伙伴們知道,這個(gè)配置實(shí)際上是一個(gè)簡(jiǎn)化的配置,點(diǎn)擊去可以看到真正配置的 Bean 是 PropertySourcesPlaceholderConfigurer,而 PropertySourcesPlaceholderConfigurer 恰好就是 BeanFactoryPostProcessor 的子類,我們來看下這里是如何重寫 postProcessBeanFactory 方法的:

源碼比較長(zhǎng),松哥這里把一些關(guān)鍵部分列出來和小伙伴們展示:

@Override
public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?throws?BeansException?{
?if?(this.propertySources?==?null)?{
?????//這里主要是如果沒有加載到?properties?文件,就會(huì)嘗試從環(huán)境中加載
?}
????//這個(gè)就是具體的屬性轉(zhuǎn)換的方法了
?processProperties(beanFactory,?new?PropertySourcesPropertyResolver(this.propertySources));
?this.appliedPropertySources?=?this.propertySources;
}
/**
?*?這個(gè)屬性轉(zhuǎn)換方法中,對(duì)配置文件又做了一些預(yù)處理,最后調(diào)用?doProcessProperties?方法處理屬性
?*/
protected?void?processProperties(ConfigurableListableBeanFactory?beanFactoryToProcess,
??final?ConfigurablePropertyResolver?propertyResolver)?throws?BeansException?{
?propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
?propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
?propertyResolver.setValueSeparator(this.valueSeparator);
?StringValueResolver?valueResolver?=?strVal?-&gt;?{
??String?resolved?=?(this.ignoreUnresolvablePlaceholders??
????propertyResolver.resolvePlaceholders(strVal)?:
????propertyResolver.resolveRequiredPlaceholders(strVal));
??if?(this.trimValues)?{
???resolved?=?resolved.trim();
??}
??return?(resolved.equals(this.nullValue)???null?:?resolved);
?};
?doProcessProperties(beanFactoryToProcess,?valueResolver);
}
protected?void?doProcessProperties(ConfigurableListableBeanFactory?beanFactoryToProcess,
??StringValueResolver?valueResolver)?{
?BeanDefinitionVisitor?visitor?=?new?BeanDefinitionVisitor(valueResolver);
?String[]?beanNames?=?beanFactoryToProcess.getBeanDefinitionNames();
?for?(String?curName?:?beanNames)?{
??//?Check?that?we're?not?parsing?our?own?bean?definition,
??//?to?avoid?failing?on?unresolvable?placeholders?in?properties?file?locations.
??if?(!(curName.equals(this.beanName)?&amp;&amp;?beanFactoryToProcess.equals(this.beanFactory)))?{
???BeanDefinition?bd?=?beanFactoryToProcess.getBeanDefinition(curName);
???try?{
????visitor.visitBeanDefinition(bd);
???}
???catch?(Exception?ex)?{
????throw?new?BeanDefinitionStoreException(bd.getResourceDescription(),?curName,?ex.getMessage(),?ex);
???}
??}
?}
?//?New?in?Spring?2.5:?resolve?placeholders?in?alias?target?names?and?aliases?as?well.
?beanFactoryToProcess.resolveAliases(valueResolver);
?//?New?in?Spring?3.0:?resolve?placeholders?in?embedded?values?such?as?annotation?attributes.
?beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}

上面第三個(gè) doProcessProperties 方法我要稍微和小伙伴們說兩句:

使用 PropertySourcesPlaceholderConfigurer 對(duì)配置中的占位符進(jìn)行處理,雖然我們只是在 DruidDataSource 中用到了相關(guān)變量,但是系統(tǒng)在處理的時(shí)候,除了當(dāng)前這個(gè)配置類之外,其他的 Bean 都要處理(因?yàn)槟憧梢栽谌我?Bean 中注入那三個(gè)變量)。

這就是 BeanFactoryPostProcessor 一個(gè)經(jīng)典實(shí)踐,即在 Bean 初始化之前,把 Bean 定義時(shí)候的一些占位符給改過來。

2.2 照貓畫虎

上面的源碼看完了,如果小伙伴們還覺得不過癮,我們自己也來寫一個(gè)試試。

我自己的需求是這樣,假設(shè)我配置 Bean 的時(shí)候,按照下面這種方式來配置:

<bean?class="org.javaboy.bean.User">
????<property?name="username"?value="^username"/>
</bean>

這里的 ^username 是我自定義的一個(gè)特殊的占位符,這個(gè)占位符表示 javaboy,我希望最終從 Spring 容器中拿到的 User Bean 的 username 屬性值是 javaboy。

為了實(shí)現(xiàn)這個(gè)需求,我可以自定義一個(gè) BeanFactoryPostProcessor,如下:

public?class?MyPropBeanFactoryPostProcessor?implements?BeanFactoryPostProcessor?{
????@Override
????public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?throws?BeansException?{
????????String[]?beanDefinitionNames?=?beanFactory.getBeanDefinitionNames();
????????for?(String?beanDefinitionName?:?beanDefinitionNames)?{
????????????BeanDefinition?beanDefinition?=?beanFactory.getBeanDefinition(beanDefinitionName);
????????????BeanDefinitionVisitor?beanDefinitionVisitor?=?new?BeanDefinitionVisitor(strVal?->?{
????????????????if?("^username".equals(strVal))?{
????????????????????return?"javaboy666";
????????????????}
????????????????return?strVal;
????????????});
????????????beanDefinitionVisitor.visitBeanDefinition(beanDefinition);
????????}
????}
}

這個(gè) Bean 基本上是照著前面 2.1 小節(jié)的 Bean 來寫的。我跟大家來大致說一下我的邏輯:

  • 首先獲取到所有的 Bean 定義對(duì)象,然后進(jìn)行遍歷。
  • 遍歷的時(shí)候創(chuàng)建一個(gè) BeanDefinitionVisitor 對(duì)象,這個(gè)對(duì)象需要一個(gè)字符解析器,也就是 lambda 表達(dá)式那一段,我這里就是說,如果傳進(jìn)來的字符串是 ^username,那么我就返回 javaboy,如果傳進(jìn)來其他值,那我原封不動(dòng),不做修改。
  • 最后調(diào)用 visitBeanDefinition 方法去重新設(shè)置一下 Bean 的定義。

上面這幾行代碼基本上就是照著 2.1 小節(jié)敲的,最后,我們把 MyPropBeanFactoryPostProcessor 注冊(cè)到 Spring 容器中就行了:

<bean?class="org.javaboy.bean.User">
????<property?name="username"?value="^username"/>
</bean>
<bean?class="org.javaboy.bean.MyPropBeanFactoryPostProcessor"/>

看下執(zhí)行效果:

2.3 BeanPostProcessor

BeanPostProcessor 主要是對(duì)一個(gè)已經(jīng)初始化的 Bean 做一些額外的配置,這個(gè)接口中包含兩個(gè)方法,執(zhí)行時(shí)間如下圖:

我寫一個(gè)簡(jiǎn)單例子我們來驗(yàn)證下:

public?class?UserService?implements?InitializingBean?{

????public?UserService()?{
????????System.out.println("UserService&gt;Constructor");
????}

????public?void?init()?{
????????System.out.println("UserService&gt;init");
????}
????@Override
????public?void?afterPropertiesSet()?throws?Exception?{
????????System.out.println("UserService&gt;afterPropertiesSet");
????}
}

再開發(fā)一個(gè) BeanPostProcessor:

public?class?MyBeanPostProcessor?implements?BeanPostProcessor?{
????@Override
????public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{
????????System.out.println("postProcessBeforeInitialization:beanName"+beanName+";beanClass:"+bean.getClass());
????????return?BeanPostProcessor.super.postProcessBeforeInitialization(bean,?beanName);
????}

????@Override
????public?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{
????????System.out.println("postProcessAfterInitialization:beanName"+beanName+";beanClass:"+bean.getClass());
????????return?BeanPostProcessor.super.postProcessAfterInitialization(bean,?beanName);
????}
}

然后我們將這兩個(gè) Bean 都注冊(cè)到 Spring 容器中:

<bean?class="org.javaboy.bean.MyBeanPostProcessor"/>
<bean?class="org.javaboy.bean.UserService"?id="us"?init-method="init">
</bean>

最后啟動(dòng)容器,來看下控制臺(tái)打印的內(nèi)容:

可以看到,跟我們所預(yù)想的是一樣的。在 MyBeanPostProcessor 中,第一個(gè)參數(shù)其實(shí)就是已經(jīng)初始化的 Bean 了,如果想在這里針對(duì) Bean 做任何修改都是可以的。

2.4 典型應(yīng)用

BeanPostProcessor 其實(shí)有很多經(jīng)典的應(yīng)用,我在寫文章的時(shí)候,想到一個(gè)地方,就是我們?cè)?SpringMVC 中做數(shù)據(jù)驗(yàn)證的時(shí)候,往往只需要加幾個(gè)注解就可以了(對(duì)此不熟悉的小伙伴可以在公眾號(hào)后臺(tái)回復(fù) ssm 有松哥錄制的 ssm 入門視頻),那么這個(gè)注解是在哪里進(jìn)行的校驗(yàn)的呢?就是 BeanPostProcessor,我們來看一眼源碼:

public?class?BeanValidationPostProcessor?implements?BeanPostProcessor,?InitializingBean?{
?@Nullable
?private?Validator?validator;
?private?boolean?afterInitialization?=?false;
?public?void?setAfterInitialization(boolean?afterInitialization)?{
??this.afterInitialization?=?afterInitialization;
?}
?@Override
?public?void?afterPropertiesSet()?{
??if?(this.validator?==?null)?{
???this.validator?=?Validation.buildDefaultValidatorFactory().getValidator();
??}
?}
?@Override
?public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{
??if?(!this.afterInitialization)?{
???doValidate(bean);
??}
??return?bean;
?}
?@Override
?public?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{
??if?(this.afterInitialization)?{
???doValidate(bean);
??}
??return?bean;
?}
?/**
??*?Perform?validation?of?the?given?bean.
??*?@param?bean?the?bean?instance?to?validate
??*?@see?jakarta.validation.Validator#validate
??*/
?protected?void?doValidate(Object?bean)?{
????????//...
?}
}

這段代碼其實(shí)很好懂,可以看到,我們可以控制是在具體是在 postProcessBeforeInitialization 還是 postProcessAfterInitialization 方法中進(jìn)行數(shù)據(jù)校驗(yàn)。

好了,現(xiàn)在小伙伴們應(yīng)該搞懂 BeanFactoryPostProcessor 和 BeanPostProcessor 的區(qū)別了吧?

3. 小結(jié)

我再把前文的小結(jié)內(nèi)容拿過來,小伙伴們現(xiàn)在看是不是更清晰了?

在 Spring 中,BeanFactoryPostProcessor 和 BeanPostProcessor 是兩個(gè)不同的接口,它們?cè)?Bean 的生命周期中扮演不同的角色。

  • BeanFactoryPostProcessor 接口用于在 Bean 工廠實(shí)例化 Bean 之前對(duì) Bean 的定義進(jìn)行修改。它可以讀取和修改 Bean 的定義元數(shù)據(jù),例如修改 Bean 的屬性值、添加額外的配置信息等。BeanFactoryPostProcessor 在 Bean 實(shí)例化之前執(zhí)行,用于對(duì) Bean 的定義進(jìn)行預(yù)處理。
  • BeanPostProcessor 接口用于在 Bean 實(shí)例化后對(duì) Bean 進(jìn)行增強(qiáng)或修改。它可以在 Bean 的初始化過程中對(duì) Bean 進(jìn)行后處理,例如對(duì) Bean 進(jìn)行代理、添加額外的功能等。BeanPostProcessor 在 Bean 實(shí)例化完成后執(zhí)行,用于對(duì) Bean 實(shí)例進(jìn)行后處理。

一言以蔽之,BeanFactoryPostProcessor 主要用于修改 Bean 的定義,而 BeanPostProcessor 主要用于增強(qiáng)或修改 Bean 的實(shí)例。

以上就是BeanFactoryPostProcessor和BeanPostProcessor區(qū)別示例分析的詳細(xì)內(nèi)容,更多關(guān)于BeanFactoryPostProcessor BeanPostProcessor區(qū)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 2020最新版SSM框架整合教程

    2020最新版SSM框架整合教程

    這篇文章主要介紹了2020最新版SSM框架整合教程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java實(shí)現(xiàn)簡(jiǎn)單的掃雷小程序

    Java實(shí)現(xiàn)簡(jiǎn)單的掃雷小程序

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的掃雷小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • Springboot接收POST請(qǐng)求,數(shù)據(jù)為json類型問題

    Springboot接收POST請(qǐng)求,數(shù)據(jù)為json類型問題

    在使用Spring框架中,當(dāng)處理POST請(qǐng)求且內(nèi)容為JSON類型時(shí),應(yīng)使用@RequestBody注解而非@RequestParam,通過@RequestBody可以將JSON數(shù)據(jù)綁定到一個(gè)Map對(duì)象中,然后通過Map的get方法來獲取需要的參數(shù)
    2022-10-10
  • Java設(shè)計(jì)模式之單例設(shè)計(jì)模式解析

    Java設(shè)計(jì)模式之單例設(shè)計(jì)模式解析

    這篇文章主要介紹了Java設(shè)計(jì)模式之單例設(shè)計(jì)模式解析,設(shè)計(jì)模式是在大量的實(shí)踐中總結(jié)和理論化之后優(yōu)選的代碼結(jié)構(gòu)、編程風(fēng)格、以及解決問題的思考方式,設(shè)計(jì)模式免去我們自己再思考和摸索,需要的朋友可以參考下
    2023-11-11
  • JavaCV實(shí)現(xiàn)多個(gè)MP4視頻的合并

    JavaCV實(shí)現(xiàn)多個(gè)MP4視頻的合并

    這篇文章主要為大家詳細(xì)介紹了如何使用javacv和ffmpeg框架實(shí)現(xiàn)簡(jiǎn)單快速的合并mp4文件的視頻和音頻,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-10-10
  • 淺談SSH框架中spring的原理

    淺談SSH框架中spring的原理

    下面小編就為大家?guī)硪黄獪\談SSH框架中spring的原理。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01
  • SpringDataRedis簡(jiǎn)單使用示例代碼

    SpringDataRedis簡(jiǎn)單使用示例代碼

    這篇文章主要介紹了SpringDataRedis簡(jiǎn)單使用,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 解決引入spring-cloud-starter-openfeign后部分類找不到的問題

    解決引入spring-cloud-starter-openfeign后部分類找不到的問題

    這篇文章主要介紹了解決引入spring-cloud-starter-openfeign后部分類找不到的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 一文搞懂JMeter engine中HashTree的配置問題

    一文搞懂JMeter engine中HashTree的配置問題

    本文主要介紹了JMeter engine中HashTree的配置,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄

    Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄

    這篇文章主要介紹了Spring Boot使用RestTemplate消費(fèi)REST服務(wù)的幾個(gè)問題記錄,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論