Spring Bean的生命周期

前言

在 Spring 中翠订,那些組成應(yīng)用程序的主體及由 Spring IOC 容器所管理的對(duì)象婚夫,被稱之為 bean。簡(jiǎn)單地講程帕,bean 就是由 IOC 容器初始化、裝配及管理的對(duì)象地啰,除此之外愁拭,bean 就與應(yīng)用程序中的其他對(duì)象沒有什么區(qū)別了。而 bean 的定義以及 bean 相互間的依賴關(guān)系將通過配置元數(shù)據(jù)來描述亏吝。

Spring中的bean默認(rèn)都是單例的岭埠,這些單例Bean在多線程程序下如何保證線程安全呢? 例如對(duì)于Web應(yīng)用來說,Web容器對(duì)于每個(gè)用戶請(qǐng)求都創(chuàng)建一個(gè)單獨(dú)的Sevlet線程來處理請(qǐng)求趁舀,引入Spring框架之后坎炼,每個(gè)Action都是單例的扒袖,那么對(duì)于Spring托管的單例Service Bean,如何保證其安全呢混聊? Spring的單例是基于BeanFactory也就是Spring容器的,單例Bean在此容器內(nèi)只有一個(gè)乾巧,Java的單例是基于 JVM句喜,每個(gè) JVM 內(nèi)只有一個(gè)實(shí)例。

bean的作用域

創(chuàng)建一個(gè)bean定義沟于,其實(shí)質(zhì)是用該bean定義對(duì)應(yīng)的類來創(chuàng)建真正實(shí)例的“配方”咳胃。把bean定義看成一個(gè)配方很有意義,它與class很類似旷太,只根據(jù)一張“處方”就可以創(chuàng)建多個(gè)實(shí)例展懈。不僅可以控制注入到對(duì)象中的各種依賴和配置值,還可以控制該對(duì)象的作用域泳秀。這樣可以靈活選擇所建對(duì)象的作用域标沪,而不必在Java Class級(jí)定義作用域。Spring Framework支持五種作用域嗜傅,分別闡述如下表金句。

類別 說明
singleton 在Spring IoC容器中僅存在一個(gè)Bean實(shí)例,Bean以單例方式存在吕嘀,默認(rèn)值
prototype 每次從容器中調(diào)用Bean時(shí)违寞,都返回一個(gè)新的實(shí)例,即每次調(diào)用getBean()時(shí)偶房,相當(dāng)于執(zhí)行new XxxBean()
request 每次HTTP請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的Bean趁曼,該該作用域僅適用于WebApplicationContext環(huán)境
session 同一個(gè)HTTP Session共享一個(gè)Bean,不同Session使用不同Bean棕洋,僅適用于WebApplicationContext環(huán)境
globalSession 一般用于Portlet應(yīng)用環(huán)境挡闰,該作用域僅適用于WebApplicationContext環(huán)境

五種作用域中,request掰盘、session 和 global session 三種作用域僅在基于web的應(yīng)用中使用(不必關(guān)心你所采用的是什么web應(yīng)用框架)摄悯,只能用在基于 web 的 Spring ApplicationContext 環(huán)境。

singleton

singleton唯一 bean 實(shí)例愧捕,當(dāng)一個(gè) bean 的作用域?yàn)?singleton奢驯,那么Spring IoC容器中只會(huì)存在一個(gè)共享的 bean 實(shí)例,并且所有對(duì) bean 的請(qǐng)求次绘,只要 id 與該 bean 定義相匹配瘪阁,則只會(huì)返回bean的同一實(shí)例撒遣。 singleton 是單例類型(對(duì)應(yīng)于單例模式),就是在創(chuàng)建起容器時(shí)就同時(shí)自動(dòng)創(chuàng)建了一個(gè)bean的對(duì)象管跺,不管你是否使用义黎,但我們可以指定Bean節(jié)點(diǎn)的 lazy-init=”true” 來延遲初始化bean,這時(shí)候伙菜,只有在第一次獲取bean時(shí)才會(huì)初始化bean轩缤,即第一次請(qǐng)求該bean時(shí)才初始化。 每次獲取到的對(duì)象都是同一個(gè)對(duì)象贩绕。注意火的,singleton 作用域是Spring中的缺省作用域。要在XML中將 bean 定義成 singleton 淑倾,可以這樣配置:

<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

也可以通過 @Scope 注解(它可以顯示指定bean的作用范圍馏鹤。)的方式

@Service
@Scope("singleton")
public class ServiceImpl{

}

prototype

prototype每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 bean 實(shí)例,當(dāng)一個(gè)bean的作用域?yàn)?prototype,表示一個(gè) bean 定義對(duì)應(yīng)多個(gè)對(duì)象實(shí)例娇哆。prototype 作用域的 bean 會(huì)導(dǎo)致在每次對(duì)該 bean 請(qǐng)求(將其注入到另一個(gè) bean 中湃累,或者以程序的方式調(diào)用容器的 getBean() 方法)時(shí)都會(huì)創(chuàng)建一個(gè)新的 bean 實(shí)例。prototype 是原型類型碍讨,它在我們創(chuàng)建容器的時(shí)候并沒有實(shí)例化治力,而是當(dāng)我們獲取bean的時(shí)候才會(huì)去創(chuàng)建一個(gè)對(duì)象,而且我們每次獲取到的對(duì)象都不是同一個(gè)對(duì)象勃黍。根據(jù)經(jīng)驗(yàn)宵统,對(duì)有狀態(tài)的 bean 應(yīng)該使用 prototype 作用域,而對(duì)無狀態(tài)的 bean 則應(yīng)該使用 singleton 作用域覆获。 在 XML 中將 bean 定義成 prototype 马澈,可以這樣配置:

<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
 或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/> 

通過 @Scope 注解的方式實(shí)現(xiàn)就不做演示了。

request

request每一次HTTP請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的bean弄息,該bean僅在當(dāng)前HTTP request內(nèi)有效,request只適用于Web程序痊班,每一次 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的bean,同時(shí)該bean僅在當(dāng)前HTTP request內(nèi)有效摹量,當(dāng)請(qǐng)求結(jié)束后涤伐,該對(duì)象的生命周期即告結(jié)束。 在 XML 中將 bean 定義成 request 缨称,可以這樣配置:

<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

session

session每一次HTTP請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean废亭,該bean僅在當(dāng)前 HTTP session 內(nèi)有效,session只適用于Web程序,session 作用域表示該針對(duì)每一次 HTTP 請(qǐng)求都會(huì)產(chǎn)生一個(gè)新的 bean具钥,同時(shí)該 bean 僅在當(dāng)前 HTTP session 內(nèi)有效.與request作用域一樣,可以根據(jù)需要放心的更改所創(chuàng)建實(shí)例的內(nèi)部狀態(tài)液兽,而別的 HTTP session 中根據(jù) userPreferences 創(chuàng)建的實(shí)例骂删,將不會(huì)看到這些特定于某個(gè) HTTP session 的狀態(tài)變化掌动。當(dāng)HTTP session最終被廢棄的時(shí)候,在該HTTP session作用域內(nèi)的bean也會(huì)被廢棄掉宁玫。

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

globalSession

global session 作用域類似于標(biāo)準(zhǔn)的 HTTP session 作用域粗恢,不過僅僅在基于 portlet 的 web 應(yīng)用中才有意義。Portlet 規(guī)范定義了全局 Session 的概念欧瘪,它被所有構(gòu)成某個(gè) portlet web 應(yīng)用的各種不同的 portle t所共享眷射。在global session 作用域中定義的 bean 被限定于全局portlet Session的生命周期范圍內(nèi)。

<bean id="user" class="com.foo.Preferences "scope="globalSession"/>

Spring Bean的生命周期

Spring容器可以管理 singleton 作用域 Bean 的生命周期佛掖,在此作用域下妖碉,Spring 能夠精確地知道該 Bean 何時(shí)被創(chuàng)建,何時(shí)初始化完成芥被,以及何時(shí)被銷毀欧宜。

而對(duì)于 prototype 作用域的 Bean,Spring 只負(fù)責(zé)創(chuàng)建拴魄,當(dāng)容器創(chuàng)建了 Bean 的實(shí)例后冗茸,Bean 的實(shí)例就交給客戶端代碼管理,Spring 容器將不再跟蹤其生命周期匹中。每次客戶端請(qǐng)求 prototype 作用域的 Bean 時(shí)夏漱,Spring 容器都會(huì)創(chuàng)建一個(gè)新的實(shí)例,并且不會(huì)管那些被配置成 prototype 作用域的 Bean 的生命周期顶捷。

了解 Spring 生命周期的意義就在于挂绰,可以利用 Bean 在其存活期間的指定時(shí)刻完成一些相關(guān)操作。這種時(shí)刻可能有很多焊切,但一般情況下扮授,會(huì)在 Bean 被初始化后和被銷毀前執(zhí)行一些相關(guān)操作。

在 Spring 中专肪,Bean 的生命周期是一個(gè)很復(fù)雜的執(zhí)行過程刹勃,我們可以利用 Spring 提供的方法定制 Bean 的創(chuàng)建過程。

當(dāng)一個(gè) Bean 被加載到 Spring 容器時(shí)嚎尤,它就具有了生命荔仁,而 Spring 容器在保證一個(gè) Bean 能夠使用之前,會(huì)進(jìn)行很多工作芽死。Spring 容器中 Bean 的生命周期流程如圖 1 所示乏梁。

Spring Bean的生命周期
Spring Bean的生命周期

Bean 生命周期的整個(gè)執(zhí)行過程描述如下:

  • 1、根據(jù)配置情況調(diào)用 Bean 構(gòu)造方法或工廠方法實(shí)例化 Bean关贵。

  • 2遇骑、利用依賴注入完成 Bean 中所有屬性值的配置注入。

  • 3揖曾、如果 Bean 實(shí)現(xiàn)了 BeanNameAware 接口落萎,則 Spring 調(diào)用 Bean 的 setBeanName() 方法傳入當(dāng)前 Bean 的 id 值亥啦。

  • 4、如果 Bean 實(shí)現(xiàn)了 BeanFactoryAware 接口练链,則 Spring 調(diào)用 setBeanFactory() 方法傳入當(dāng)前工廠實(shí)例的引用翔脱。

  • 5、如果 Bean 實(shí)現(xiàn)了 ApplicationContextAware 接口媒鼓,則 Spring 調(diào)用 setApplicationContext() 方法傳入當(dāng)前 ApplicationContext 實(shí)例的引用届吁。

  • 6、如果 BeanPostProcessor 和 Bean 關(guān)聯(lián)绿鸣,則 Spring 將調(diào)用該接口的預(yù)初始化方法 postProcessBeforeInitialzation() 對(duì) Bean 進(jìn)行加工操作疚沐,此處非常重要,Spring 的 AOP 就是利用它實(shí)現(xiàn)的枚驻。

  • 7濒旦、如果 Bean 實(shí)現(xiàn)了 InitializingBean 接口,則 Spring 將調(diào)用 afterPropertiesSet() 方法再登。

  • 8尔邓、如果在配置文件中通過 init-method 屬性指定了初始化方法,則調(diào)用該初始化方法锉矢。

  • 9梯嗽、如果 BeanPostProcessor 和 Bean 關(guān)聯(lián),則 Spring 將調(diào)用該接口的初始化方法 postProcessAfterInitialization()沽损。此時(shí)灯节,Bean 已經(jīng)可以被應(yīng)用系統(tǒng)使用了。

  • 10绵估、如果在 <bean> 中指定了該 Bean 的作用范圍為 scope="singleton"炎疆,則將該 Bean 放入 Spring IoC 的緩存池中,將觸發(fā) Spring 對(duì)該 Bean 的生命周期管理国裳;如果在 <bean> 中指定了該 Bean 的作用范圍為 scope="prototype"形入,則將該 Bean 交給調(diào)用者,調(diào)用者管理該 Bean 的生命周期缝左,Spring 不再管理該 Bean亿遂。

  • 11、如果 Bean 實(shí)現(xiàn)了 DisposableBean 接口渺杉,則 Spring 會(huì)調(diào)用 destory() 方法將 Spring 中的 Bean 銷毀蛇数;如果在配置文件中通過 destory-method 屬性指定了 Bean 的銷毀方法,則 Spring 將調(diào)用該方法對(duì) Bean 進(jìn)行銷毀是越。

Spring 為 Bean 提供了細(xì)致全面的生命周期過程耳舅,通過實(shí)現(xiàn)特定的接口或 <bean> 的屬性設(shè)置,都可以對(duì) Bean 的生命周期過程產(chǎn)生影響倚评。雖然可以隨意配置 <bean> 的屬性挽放,但是建議不要過多地使用 Bean 實(shí)現(xiàn)接口绍赛,因?yàn)檫@樣會(huì)導(dǎo)致代碼和 Spring 的聚合過于緊密。

BeanPostProcessor

該接口我們也叫后置處理器辑畦,作用是在Bean對(duì)象在實(shí)例化和依賴注入完畢后,在顯示調(diào)用初始化方法的前后添加我們自己的邏輯腿倚。注意是Bean實(shí)例化完畢后及依賴注入完成后觸發(fā)的纯出。接口的源碼如下:

public interface BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other BeanPostProcessor callbacks.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
方法 說明
postProcessBeforeInitialization 實(shí)例化、依賴注入完畢敷燎,在調(diào)用顯示的初始化之前完成
一些定制的初始化任務(wù)
postProcessAfterInitialization 實(shí)例化暂筝、依賴注入、初始化完畢時(shí)執(zhí)行

一硬贯、自定義后置處理器演示

1焕襟、自定義處理器

package com.dpb.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
 * 自定義BeanPostProcessor實(shí)現(xiàn)類
 * BeanPostProcessor接口的作用是:
 *   我們可以通過該接口中的方法在bean實(shí)例化、配置以及其他初始化方法前后添加一些我們自己的邏輯
 * @author dengp
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor{

    /**
     * 實(shí)例化饭豹、依賴注入完畢鸵赖,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before--實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    /**
     * 實(shí)例化拄衰、依賴注入它褪、初始化完畢時(shí)執(zhí)行 
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after...實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

}

注意:接口中兩個(gè)方法不能返回null翘悉,如果返回null那么在后續(xù)初始化方法將報(bào)空指針異趁4颍或者通過getBean()方法獲取不到bena實(shí)例對(duì)象,因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中妖混。

2老赤、Pojo類

public class User {

    private int id;
    
    private String name;
    
    private String beanName;
    
    public User(){
        System.out.println("User 被實(shí)例化");
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("設(shè)置:"+name);
        this.name = name;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
    /**
     * 自定義的初始化方法
     */
    public void start(){
        System.out.println("User 中自定義的初始化方法");
    }
}

3、配置文件注冊(cè)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.dpb.pojo.User" id="user" init-method="start">
        <property name="name" value="波波烤鴨" />
    </bean>
    
    <!-- 注冊(cè)處理器 -->
    <bean class="com.dpb.processor.MyBeanPostProcessor"></bean>
</beans>

4制市、測(cè)試

@Test
public void test() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = ac.getBean(User.class);
    System.out.println(user);
}

輸出結(jié)果:

User 被實(shí)例化
設(shè)置:波波烤鴨
before--實(shí)例化的bean對(duì)象:com.dpb.pojo.User@65e2dbf3   user
User 中自定義的初始化方法
after...實(shí)例化的bean對(duì)象:com.dpb.pojo.User@65e2dbf3   user
com.dpb.pojo.User@65e2dbf3

通過輸出語句我們也能看到postProcessBeforeInitialization方法的輸出語句是在Bean實(shí)例化及屬性注入后執(zhí)行的抬旺,且在自定義的初始化方法之前執(zhí)行(通過init-method指定)。而postProcessAfterInitialization方法是在自定義初始化方法執(zhí)行之后執(zhí)行的息堂。

注意:

  • BeanFactory和ApplicationContext兩個(gè)容器對(duì)待bean的后置處理器稍微有些不同嚷狞。ApplicationContext容器會(huì)自動(dòng)檢測(cè)Spring配置文件中那些bean所對(duì)應(yīng)的Java類實(shí)現(xiàn)了BeanPostProcessor接口,并自動(dòng)把它們注冊(cè)為后置處理器荣堰。在創(chuàng)建bean過程中調(diào)用它們床未,所以部署一個(gè)后置處理器跟普通的bean沒有什么太大區(qū)別。
  • BeanFactory容器注冊(cè)bean后置處理器時(shí)必須通過代碼顯示的注冊(cè)振坚,在IoC容器繼承體系中的ConfigurableBeanFactory接口中定義了注冊(cè)方法
/**
 * Add a new BeanPostProcessor that will get applied to beans created
 * by this factory. To be invoked during factory configuration.
 * <p>Note: Post-processors submitted here will be applied in the order of
 * registration; any ordering semantics expressed through implementing the
 * {@link org.springframework.core.Ordered} interface will be ignored. Note
 * that autodetected post-processors (e.g. as beans in an ApplicationContext)
 * will always be applied after programmatically registered ones.
 * @param beanPostProcessor the post-processor to register
 */
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

測(cè)試代碼如下

@Test
public void test2() {
    //ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    // 顯示添加后置處理器
    bf.addBeanPostProcessor(bf.getBean(MyBeanPostProcessor.class));
    User user = bf.getBean(User.class);
    System.out.println(user);
}

二薇搁、多個(gè)后置處理器

我們可以在Spring配置文件中添加多個(gè)BeanPostProcessor(后置處理器)接口實(shí)現(xiàn)類,在默認(rèn)情況下Spring容器會(huì)根據(jù)后置處理器的定義順序來依次調(diào)用渡八。

public class MyBeanPostProcessor implements BeanPostProcessor{

    /**
     * 實(shí)例化啃洋、依賴注入完畢传货,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A before--實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    /**
     * 實(shí)例化宏娄、依賴注入问裕、初始化完畢時(shí)執(zhí)行 
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A after...實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }
}
public class MyBeanPostProcessor2 implements BeanPostProcessor{

    /**
     * 實(shí)例化孵坚、依賴注入完畢粮宛,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B before--實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    /**
     * 實(shí)例化卖宠、依賴注入巍杈、初始化完畢時(shí)執(zhí)行 
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B after...實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }
}

配置文件注冊(cè)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.dpb.pojo.User" id="user" init-method="start">
        <property name="name" value="波波烤鴨" />
    </bean>
    
    <!-- 注冊(cè)處理器 -->
    <bean class="com.dpb.processor.MyBeanPostProcessor"/>
    <bean class="com.dpb.processor.MyBeanPostProcessor2"/>
</beans>

測(cè)試結(jié)果:

User 被實(shí)例化
設(shè)置:波波烤鴨
A before--實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
B before--實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
User 中自定義的初始化方法
A after...實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
B after...實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
com.dpb.pojo.User@7fac631b

三扛伍、顯示指定順序

在Spring機(jī)制中可以指定后置處理器調(diào)用順序筷畦,通過讓BeanPostProcessor接口實(shí)現(xiàn)類實(shí)現(xiàn)Ordered接口getOrder方法,該方法返回一整數(shù)刺洒,默認(rèn)值為 0鳖宾,優(yōu)先級(jí)最高,值越大優(yōu)先級(jí)越低

public class MyBeanPostProcessor implements BeanPostProcessor,Ordered{

    /**
     * 實(shí)例化作媚、依賴注入完畢攘滩,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A before--實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    /**
     * 實(shí)例化纸泡、依賴注入漂问、初始化完畢時(shí)執(zhí)行 
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("A after...實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    @Override
    public int getOrder() {
        // TODO Auto-generated method stub
        return 10;
    }
}
public class MyBeanPostProcessor2 implements BeanPostProcessor,Ordered{

    /**
     * 實(shí)例化女揭、依賴注入完畢蚤假,在調(diào)用顯示的初始化之前完成一些定制的初始化任務(wù)
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B before--實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    /**
     * 實(shí)例化吧兔、依賴注入磷仰、初始化完畢時(shí)執(zhí)行 
     * 注意:方法返回值不能為null
     * 如果返回null那么在后續(xù)初始化方法將報(bào)空指針異常或者通過getBean()方法獲取不到bena實(shí)例對(duì)象
     * 因?yàn)楹笾锰幚砥鲝腟pring IoC容器中取出bean實(shí)例對(duì)象沒有再次放回IoC容器中
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("B after...實(shí)例化的bean對(duì)象:"+bean+"\t"+beanName);
        // 可以根據(jù)beanName不同執(zhí)行不同的處理操作
        return bean;
    }

    @Override
    public int getOrder() {
        // TODO Auto-generated method stub
        return 2;
    }
}

測(cè)試輸出結(jié)果:

User 被實(shí)例化
設(shè)置:波波烤鴨
B before--實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
A before--實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
User 中自定義的初始化方法
B after...實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
A after...實(shí)例化的bean對(duì)象:com.dpb.pojo.User@7fac631b user
com.dpb.pojo.User@7fac631b

數(shù)值越大的優(yōu)先級(jí)越低境蔼,所以A的輸出就在后面了灶平。

參考:
http://c.biancheng.net/view/4261.html

https://www.cnblogs.com/dengpengbo/p/10464892.html

https://www.cnblogs.com/yoci/p/10642553.html

https://my.oschina.net/u/3905482/blog/1859227

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箍土,隨后出現(xiàn)的幾起案子逢享,更是在濱河造成了極大的恐慌,老刑警劉巖吴藻,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞒爬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侧但,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門矢空,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人禀横,你說我怎么就攤上這事屁药。” “怎么了燕侠?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵者祖,是天一觀的道長。 經(jīng)常有香客問我绢彤,道長,這世上最難降的妖魔是什么蜓耻? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任茫舶,我火速辦了婚禮,結(jié)果婚禮上刹淌,老公的妹妹穿的比我還像新娘饶氏。我一直安慰自己,他們只是感情好有勾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布疹启。 她就那樣靜靜地躺著,像睡著了一般蔼卡。 火紅的嫁衣襯著肌膚如雪喊崖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天雇逞,我揣著相機(jī)與錄音荤懂,去河邊找鬼。 笑死塘砸,一個(gè)胖子當(dāng)著我的面吹牛节仿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掉蔬,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼廊宪,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了女轿?” 一聲冷哼從身側(cè)響起箭启,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谈喳,沒想到半個(gè)月后册烈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年赏僧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了大猛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淀零,死狀恐怖挽绩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驾中,我是刑警寧澤唉堪,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站肩民,受9級(jí)特大地震影響唠亚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜持痰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一灶搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧工窍,春花似錦割卖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至淹仑,卻和暖如春丙挽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背攻人。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工取试, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怀吻。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓瞬浓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蓬坡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子猿棉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349