溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring生命周期回調(diào)與容器擴展的示例分析

發(fā)布時間:2021-08-25 10:13:15 來源:億速云 閱讀:136 作者:小新 欄目:編程語言

小編給大家分享一下Spring生命周期回調(diào)與容器擴展的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

下面主要介紹:

類級別的生命周期初始化回調(diào)方法init-method配置、InitializingBean接口和PostConstruct注解

容器級別的擴展BeanPostProcessor接口和BeanFactoryPostProcessor接口

1.類級別生命周期回調(diào)

1.1init-method

參照:Springbeanxsdinit-method

init-method是在Spring配置文件中聲明bean的時候的一個配置項。init-method配置項的值為類中的一個無參方法,但可拋出異常。該方法會在Spring容器實例化對象并設(shè)置完屬性值之后被調(diào)用。

init-method能實現(xiàn)的功能與InitializingBean接口、PostConstruct注解一致

Spring配置文件及測試類如下:

<bean id = "initMethodBeanService" class="name.liuxi.spring.ext.InitMethodBeanService" init-method="init">
   <property name="f2" value="2"/>
</bean>

測試類如下:

public class InitMethodBeanService {
	private static Integer f1;
	private Integer f2;
	static {
		f1 = 1;
		System.out.println("InitMethodBeanService static block execute...");
	}
	public InitMethodBeanService(){
		System.out.println("InitMethodBeanService construct method execute...");
	}
	public void init(){
		System.out.println("InitMethodBeanService init method execute...");
	}
	public Integer getF2() {
		return f2;
	}
	public void setF2(Integer f2) {
		this.f2 = f2;
		System.out.println("InitMethodBeanService setF2 method execute...");
	}
}

執(zhí)行結(jié)果打印如下:

InitMethodBeanService static block execute...
InitMethodBeanService construct method execute...
InitMethodBeanService setF2 method execute...
InitMethodBeanService init method execute...
test method execute...

1.2InitializingBean接口

參照:Spring官方文檔beans-factory-lifecycle-initializingbean

InitializingBean接口中聲明了一個方法afterPropertiesSet,該方法會在Spring容器實例化對象并設(shè)置完屬性值之后被調(diào)用。和上面的init-method實現(xiàn)的功能一致,因此Spring不推薦使用InitializingBean接口。

例子比較簡單,不列出來了

1.3PostConstruct注解

翻譯:Spring官方文檔beans-postconstruct-and-predestroy-annotations

@PostConstruct注解是和init-method、InitializingBean接口實現(xiàn)效果一致的生命周期回調(diào)方法

@PostConstruct
public void postConstruct(){
  System.out.println("PostConstructService postConstruct method execute...");
}

總結(jié)下上面三個生命周期回調(diào)方法init-method、InitializingBean接口、@PostConstruct注解

1.都是針對單個類的實例化后處理

2.執(zhí)行時間都是在類實例化完成,且成員變量完成注入之后調(diào)用的

3.對于init-method,還可以在Spring配置文件的beans元素下配置默認(rèn)初始化方法,配置項為default-init-method

4.若以上三種方式配置的初始化方法都不一樣,則執(zhí)行順序為:@PostConstruct注解方法–>InitializingBean的afterPropertiesSet–>init-method方法;若三種方式配置的方法一樣,則方法只執(zhí)行一次(參照:Spring官方文檔beans-factory-lifecycle-combined-effect)

5.有初始化回調(diào)方法,對應(yīng)的也有銷毀的回調(diào)方法。@PostConstruct注解方法–>InitializingBean的afterPropertiesSet–>init-method方法分別對應(yīng)@PreDestroy注解方法–>DisposableBean的destroy–>destroy-method方法

2.容器級別擴展

翻譯:Spring官方文檔3.8ContainerExtensionPoints

通常情況下,開發(fā)人員無需自定義實現(xiàn)一個ApplicationContext的子類去擴展SpringIOC容器,SpringIOC容器通過對外暴露的一些接口,可實現(xiàn)對SpringIOC容器的擴展。

2.1BeanPostProcessor接口

2.1.1bean實例初始化后處理器及后處理器鏈

BeanPostProcessor接口定義了兩個容器級別的回調(diào)方法postProcessBeforeInitialization和postProcessAfterInitialization,用于在初始化實例后的一些邏輯處理,會針對容器中的所有實例進行處理。實現(xiàn)了BeanPostProcessor接口的類,稱之為bean實例初始化后處理器。

若在SpringIOC容器中集成了多個實例初始化后處理器,這些后處理器構(gòu)成的集合稱之為bean實例初始化后處理器鏈。

postProcessBeforeInitialization方法在類實例化且成員變量注入完成之后執(zhí)行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之前執(zhí)行

postProcessAfterInitialization方法在類實例化且成員變量注入完成之后執(zhí)行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之后執(zhí)行

總結(jié):

1.實例初始化后處理器多用于對實例的一些代理操作。Spring中一些使用到AOP的特性也是通過后處理器的方式實現(xiàn)的。

2.實例初始化后處理器鏈?zhǔn)嵌鄠€后處理器,就會有執(zhí)行順序的問題,可以通過實現(xiàn)Ordered接口,指定后處理的執(zhí)行順序,Ordered接口聲明了getOrder方法,方法返回值越小,后處理的優(yōu)先級越高,越早執(zhí)行。

3.在通過實現(xiàn)BeanPostProcessor接口自定義實例初始化后處理器的時候,建議也實現(xiàn)Ordered接口,指定優(yōu)先級。

4.這些后處理器的作用域是當(dāng)前的SpringIOC容器,即后處理器被聲明的SpringIOC容器。對于有層次結(jié)構(gòu)的SpringIOC容器,實例初始化后處理器鏈不會作用于其他容器所初始化的實例上,即使兩個容器在同一層次結(jié)構(gòu)上。

5.實例初始化后處理器的實現(xiàn)類只需要和普通的被Spring管理的bean一樣聲明,SpringIOC容器就會自動檢測到,并添加到實例初始化后處理器鏈中。

6.相對于自動檢測,我們也可以調(diào)用ConfigurableBeanFactory的addBeanPostProcessor方法,以編程的方式將一個實例初始化后處理器添加到實例初始化后處理器鏈中。這在需要判定添加條件的場景下比較實用。這種編程式的方式會忽略到實現(xiàn)的Ordered接口所指定的順序,而會作用于所有的被自動檢測的實例初始化后處理器之前。

2.1.2bean實例初始化后處理器與AOP

BeanPostProcessor是一個特殊的接口,實現(xiàn)這個接口的類會被作為Spring管理的bean的實例的后處理器。因此,在Spring應(yīng)用上下文啟動的一個特殊階段,會直接初始化所有實現(xiàn)了BeanPostProcessor接口的實例,以及該實例所引用的類也會被實例化。然后作為后處理器應(yīng)用于其他普通實例。

由于AOP的自動代理是以實例化后處理器的方式實現(xiàn)的,所以無論是bean實例初始化后處理器鏈實例還是其引用的實例,都不能被自動代理。因而,不要在這些實例上進行切面織入。(對于這些實例,會產(chǎn)生這樣的日志消息:“類foo不能被所有的實例化后處理器鏈處理,即不能被自動代理”)。

注意:當(dāng)實例化后處理器以autowiring或@Resource的方式引用其他bean,Spring容器在以類型匹配依賴注入的時候,可能會注入非指定的bean(例如:實例化后處理器實現(xiàn)類以Resource方式依賴bean,若set方法和被依賴的bean的名稱一致或者被依賴bean未聲明名稱,則依賴注入會以類型匹配的方式注入,此時可能會注入非指定的bean)。這也會導(dǎo)致自動代理或其他方式的實例化后處理器處理失敗。

2.1.3bean實例初始化后處理器示例

public class BeanPostProcessorService implements BeanPostProcessor {
	@Override
	  public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
		System.out.println("BeanPostProcessorService postProcessAfterInitialization method execute... ");
		return o;
	}
	@Override
	  public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
		System.out.println("BeanPostProcessorService postProcessBeforeInitialization method execute... ");
		return o;
	}
}

2.2BeanFactoryPostProcessor接口

2.2.1beanfactory后處理器

通過實現(xiàn)BeanFactoryPostProcessor接口,可以讀取容器所管理的bean的配置元數(shù)據(jù),在bean完成實例化之前進行更改,這些bean稱之為beanfactory后處理器。

BeanFactoryPostProcessors與BeanPostProcessor接口的異同點:

相同點:

都是容器級別的后處理器

都可配置多個后處理器,并通過實現(xiàn)Ordered接口,指定執(zhí)行順序

都是針對接口聲明的容器中所管理的bean進行處理,在有層級結(jié)構(gòu)的容器中,不能處理其他容器中的bean,即使兩個容器是同一層次

都是只需要在容器中和普通bean一樣聲明,容器會自動檢測到,并注冊為后處理器

會忽略延遲初始化屬性配置

不同點:

BeanFactoryPostProcessors接口在bean**實例化前處理bean的配置元數(shù)據(jù),BeanPostProcessor接口在bean實例化后處理bean的實例**

BeanFactoryPostProcessors接口也能通過BeanFactory.getBean()方法獲取bean的實例,這樣會引起bean的實例化。由于BeanFactoryPostProcessors后處理器是在所有bean實例化之前執(zhí)行,通過BeanFactory.getBean()方法會導(dǎo)致提前實例化bean,從而打破容器標(biāo)準(zhǔn)的生命周期,這樣可能會引起一些負(fù)面的影響(例如:提前實例化的bean會忽略bean實例化后處理器的處理)。

2.2.2Spring內(nèi)置及自定義beanfactory后處理器

Spring內(nèi)置了一些beanfactory后處理器(例如:PropertyPlaceholderConfigurer和PropertyOverrideConfigurer)。同時也支持實現(xiàn)BeanFactoryPostProcessor接口,自定義beanfactory后處理器。下面說說Spring內(nèi)置的兩個后處理器和自定義后處理器。

PropertyPlaceholderConfigurer

Spring為了避免主要的XML定義文件的修改而引起的風(fēng)險,提供了配置分離,可以將一些可能變更的變量配置到屬性配置文件中,并在XML定義文件中以占位符的方式引用。這樣,修改配置只需要修改屬性配置文件即可。PropertyPlaceholderConfigurer用于檢測占位符,并替換占位符為配置屬性值。示例如下:

PropertyPlaceholderConfigurer通過jdbc.properties屬性配置文件,在運行時,將dataSource這個bean中數(shù)據(jù)庫相關(guān)信息的屬性占位符替換成對應(yīng)的配置值。

XML配置如下:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
    class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>

屬性配置文件jdbc.properties如下:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

PropertyPlaceholderConfigurer不僅支持屬性配置文件的讀取,也支持讀取系統(tǒng)屬性。通過systemPropertiesMode屬性值可配置讀取優(yōu)先級。各種取值說明如下:

0:不讀取系統(tǒng)屬性

1:若引用的屬性配置文件中未檢索到對應(yīng)占位符的配置,則讀取系統(tǒng)屬性。默認(rèn)為1

2:先讀取系統(tǒng)屬性,再讀取引用的屬性配置文件。這種配置可能導(dǎo)致系統(tǒng)屬性覆蓋配置文件。

PropertyOverrideConfigurer

PropertyOverrideConfigurer類可以通過引用屬性配置文件,直接給容器中的bean賦值。當(dāng)一個bean的屬性被多個PropertyOverrideConfigurer類實例賦值時,最后一個的值會覆蓋前面的。

還是以上面給上面的dataSource的bean賦值為例:

PropertyOverrideConfigurer類對屬性配置文件的引用使用一個新的方式,如下:

<context:property-override location="classpath:override.properties"/>

override.properties屬性配置文件的屬性的命名規(guī)則和上面不同(上面例子中需要保證屬性名和占位符一致),命名規(guī)則是beanName.property

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
dataSource.username=sa
dataSource.password=root

支持復(fù)合屬性的賦值,但是要保證引用被賦值屬性的對象非空
例如:foo.fred.bob.sammy=123
自定義bean factory后處理器

自定義bean factory后處理器就是實現(xiàn)BeanFactoryPostProcessor接口,完成對Spring容器管理的bean的配置元數(shù)據(jù)進行修改。例如:修改類屬性注入的值,示例如下:

定義一個用戶類UserBean

public class UserBean {
	private String userName;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
}

Spring XML配置文件配置用戶類,并給用戶名屬性userName注入值haha

<bean class="name.liuxi.spring.ext.BeanFactoryPostProcessorService"/>
<bean id="user" class="name.liuxi.spring.ext.UserBean">
   <property name="userName" value="haha"/>
</bean>

下面是自定義的bean factory后處理器,修改屬性userName的值為heihei

public class BeanFactoryPostProcessorService implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    System.out.println("BeanFactoryPostProcessorService postProcessBeanFactory method execut...");
    BeanDefinition bd = beanFactory.getBeanDefinition("user");
    MutablePropertyValues pv = bd.getPropertyValues();
    if(pv.contains("userName"))
    {
      pv.addPropertyValue("userName", "heihei");
    }
  }
}

以上是“Spring生命周期回調(diào)與容器擴展的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI