bean定義能夠包含許多配置信息,包括構造函數參數,屬性值和特定於容器的信息,例如初始化方法,靜態工廠方法名稱等。子bean定義從父定義繼承配置數據。子定義能夠覆蓋某些值或根據須要添加其餘值。使用父bean和子bean定義能夠節省大量的輸入。實際上,這是一種模板形式。php
若是以ApplicationContext
編程方式使用接口,則子bean定義由ChildBeanDefinition
類表示。大多數用戶不在此級別上使用它們。相反,它們在類中以聲明方式配置bean定義ClassPathXmlApplicationContext
。使用基於XML的配置元數據時,可使用該parent
屬性指定子bean定義,並將父bean指定爲此屬性的值。如下示例顯示瞭如何執行此操做:html
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
複製代碼
注意
parent
屬性。java
若是沒有指定,則bean bean定義使用父定義中的bean類,但也能夠覆蓋它。在後一種狀況下,子bean類必須與父類兼容(即,它必須接受父類的屬性值)。mysql
子bean定義從父級繼承範圍,構造函數參數值,屬性值和方法覆蓋,並帶有添加新值的選項。static
您指定的任何範圍,初始化方法,銷燬方法或工廠方法設置都會覆蓋相應的父設置。spring
其他設置始終取自子定義:取決於,autowire模式,依賴性檢查,單例和惰性初始化。sql
前面的示例經過使用該abstract
屬性將父bean定義顯式標記爲abstract 。若是父定義未指定類,abstract
則根據須要顯式標記父bean定義,如如下示例所示:數據庫
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
複製代碼
父bean不能單獨實例化,由於它不完整,而且也明確標記爲abstract
。定義時abstract
,它僅可用做純模板bean定義,用做子定義的父定義。嘗試使用這樣的abstract
父bean,經過將其稱爲另外一個bean的ref屬性或getBean()
使用父bean ID 進行顯式調用,將返回錯誤。相似地,容器的內部 preInstantiateSingletons()
方法忽略定義爲abstract的bean定義。apache
ApplicationContext
默認狀況下預先實例化全部單例。所以,重要的是(至少對於單例bean),若是你有一個(父)bean定義,你只打算用做模板,而且這個定義指定了一個類,你必須確保將abstract屬性設置爲true不然應用程序上下文將實際(嘗試)預先實例化abstract
bean。編程
一般,應用程序開發人員不須要子類化ApplicationContext
實現類。相反,能夠經過插入特殊集成接口的實現來擴展Spring IoC容器。接下來的幾節將介紹這些集成接口。api
BeanPostProcessor
該BeanPostProcessor
接口定義了您能夠實現的回調方法,以提供您本身的(或覆蓋容器的默認)實例化邏輯,依賴關係解析邏輯等。若是要在Spring容器完成實例化,配置和初始化bean以後實現某些自定義邏輯,則能夠插入一個或多個自定義BeanPostProcessor
實現。
您能夠配置多個BeanPostProcessor
實例,而且能夠BeanPostProcessor
經過設置order
屬性來控制這些實例的執行順序。只有在BeanPostProcessor
實現Ordered
接口時才能設置此屬性。若是你本身編寫BeanPostProcessor
,你也應該考慮實現這個Ordered
接口。有關更多詳細信息,請參閱BeanPostProcessor
和Ordered
接口的javadoc 。另見關於實例的程序化登記BeanPostProcessor
的說明。
BeanPostProcessor
實例在bean(或對象)實例上運行。也就是說,Spring IoC容器實例化一個bean實例,而後
BeanPostProcessor實例執行它們的工做。
BeanPostProcessor實例的範圍是每一個容器。僅當您使用容器層次結構時,這纔是相關的。若是
BeanPostProcessor在一個容器中定義一個容器,它只會對該容器中的bean進行後處理。換句話說,
BeanPostProcessor即便兩個容器都是同一層次結構的一部分,在一個容器中定義的bean也不會被另外一個容器中定義的bean進行後處理。要更改實際的bean定義(即定義bean的藍圖),您須要使用a
BeanFactoryPostProcessor,如 使用。[
定製配置元數據中所述`BeanFactoryPostProcessor](docs.spring.io/spring/docs…)
該org.springframework.beans.factory.config.BeanPostProcessor
接口由兩個回調方法組成。當這樣的類被註冊爲具備容器的後處理器時,對於由容器建立的每一個bean實例,後處理器在容器初始化方法(例如InitializingBean.afterPropertiesSet()
或任何聲明的init
方法)以前都從容器得到回調。調用,並在任何bean初始化後回調。後處理器能夠對bean實例執行任何操做,包括徹底忽略回調。bean後處理器一般檢查回調接口,或者它能夠用代理包裝bean。一些Spring AOP基礎結構類實現爲bean後處理器,以便提供代理包裝邏輯。
的ApplicationContext
自動檢測中的實現的配置的元數據中定義的任何豆BeanPostProcessor
接口。將 ApplicationContext
這些bean註冊爲後處理器,以便在建立bean時能夠稍後調用它們。Bean後處理器能夠以與任何其餘bean相同的方式部署在容器中。
請注意,在配置類上BeanPostProcessor
使用@Bean
工廠方法聲明a時,工廠方法的返回類型應該是實現類自己或至少是org.springframework.beans.factory.config.BeanPostProcessor
接口,清楚地代表該bean的後處理器性質。不然,ApplicationContext
在徹底建立以前, 沒法按類型自動檢測它。因爲BeanPostProcessor
須要儘早實例化以便應用於上下文中其餘bean的初始化,所以這種早期類型檢測相當重要。
以編程方式註冊
BeanPostProcessor
實例雖然推薦的BeanPostProcessor
註冊方法是經過ApplicationContext
自動檢測(如前所述),但您能夠ConfigurableBeanFactory
使用該addBeanPostProcessor
方法以編程方式對其進行註冊。當您須要在註冊前評估條件邏輯或甚至跨層次結構中的上下文複製Bean post處理器時,這很是有用。但請注意,以BeanPostProcessor
編程方式添加的實例不尊重Ordered
接口。這裏,註冊的順序決定了執行的順序。另請注意,以BeanPostProcessor
編程方式註冊的實例始終在經過自動檢測註冊的實例以前處理,而無論任何顯式排序。
BeanPostProcessor
實例和AOP自動代理實現
BeanPostProcessor接口的類是特殊的,容器會對它們進行不一樣的處理。
BeanPostProcessor他們直接引用的全部實例和bean都會在啓動時實例化,做爲特殊啓動階段的一部分
ApplicationContext。接下來,全部
BeanPostProcessor實例都以排序方式註冊,並應用於容器中的全部其餘bean。由於AOP自動代理是做爲一個
BeanPostProcessor自身實現的,因此
BeanPostProcessor實例和它們直接引用的bean都不符合自動代理的條件,所以沒有編織方面。對於任何此類bean,您應該看到一條信息性日誌消息:
Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。若是您
BeanPostProcessor經過使用自動裝配或
@Resource(可能回退到自動裝配)將bean鏈接到您的 ,則Spring可能會在搜索類型匹配依賴項候選項時訪問意外的bean,所以,使它們不符合自動代理或其餘類型的bean post -處理。例如,若是您有一個依賴項,
@Resource`其中字段或setter名稱與bean的聲明名稱沒有直接對應,而且沒有使用name屬性,則Spring會訪問其餘bean以按類型匹配它們。
如下示例顯示如何在中編寫,註冊和使用BeanPostProcessor
實例ApplicationContext
。
BeanPostProcessor
-style第一個例子說明了基本用法。該示例顯示了一個自定義 BeanPostProcessor
實現,該實現調用toString()
容器建立的每一個bean 的方法,並將生成的字符串輸出到系統控制檯。
如下清單顯示了自定義BeanPostProcessor
實現類定義:
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
複製代碼
如下beans
元素使用InstantiationTracingBeanPostProcessor
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger" script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!-- when the above bean (messenger) is instantiated, this custom BeanPostProcessor implementation will output the fact to the system console -->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
複製代碼
請注意它InstantiationTracingBeanPostProcessor
是如何定義的。它甚至沒有名稱,而且,由於它是一個bean,它能夠像任何其餘bean同樣依賴注入。(前面的配置還定義了一個由Groovy腳本支持的bean。在動態語言支持一章中詳細介紹了Spring 動態語言支持。)
如下Java應用程序運行上述代碼和配置:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
複製代碼
上述應用程序的輸出相似於如下內容:
Bean'sensenger'建立:org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
複製代碼
RequiredAnnotationBeanPostProcessor
將回調接口或註釋與自定義BeanPostProcessor
實現結合使用 是擴展Spring IoC容器的經常使用方法。一個例子是Spring RequiredAnnotationBeanPostProcessor
- 一個 BeanPostProcessor
隨Spring發行版一塊兒提供的實現,它確保標記有(任意)註釋的bean上的JavaBean屬性實際上(配置爲)依賴注入值。
BeanFactoryPostProcessor
咱們看到的下一個擴展點是 org.springframework.beans.factory.config.BeanFactoryPostProcessor
。這個接口的語義相似於BeanPostProcessor
它的一個主要區別:BeanFactoryPostProcessor
操做bean配置元數據。也就是說,Spring IoC容器容許BeanFactoryPostProcessor
讀取配置元數據,並可能在容器實例化除實例以外的任何bean 以前更改它BeanFactoryPostProcessor
。
您能夠配置多個BeanFactoryPostProcessor
實例,而且能夠BeanFactoryPostProcessor
經過設置order
屬性來控制這些實例的運行順序。可是,若是BeanFactoryPostProcessor
實現 Ordered
接口,則只能設置此屬性。若是你本身編寫BeanFactoryPostProcessor
,你也應該考慮實現這個Ordered
接口。有關更多詳細信息,請參閱BeanFactoryPostProcessor
和Ordered
接口的javadoc 。
若是要更改實際的bean實例(即,從配置元數據建立的對象),則須要使用a
BeanPostProcessor
(前面在使用a定製Bean中進行了描述BeanPostProcessor
)。雖然技術上能夠在a中使用bean實例BeanFactoryPostProcessor
(例如,經過使用BeanFactory.getBean()
),但這樣作會致使過早的bean實例化,從而違反標準的容器生命週期。這可能會致使負面影響,例如繞過bean後期處理。此外,BeanFactoryPostProcessor
實例的範圍是每一個容器的範圍。僅當您使用容器層次結構時,這纔有意義。若是BeanFactoryPostProcessor
在一個容器中定義一個容器,則它僅應用於該容器中的bean定義。BeanFactoryPostProcessor
即便兩個容器都是同一層次結構的一部分,一個容器中的Bean定義也不會被另外一個容器中的實例進行後處理。
Bean工廠後處理器在其內部聲明時會自動執行 ApplicationContext
,以便將更改應用於定義容器的配置元數據。Spring包含許多預約義的bean工廠後處理器,例如PropertyOverrideConfigurer
和 PropertyPlaceholderConfigurer
。您還可使用自定義BeanFactoryPostProcessor
- 例如,註冊自定義屬性編輯器。
一個ApplicationContext
自動檢測部署在它實現了任何豆BeanFactoryPostProcessor
接口。它在適當的時候使用這些bean做爲bean工廠後處理器。您能夠像處理任何其餘bean同樣部署這些後處理器bean。
與
BeanPostProcessor
s同樣,您一般不但願BeanFactoryPostProcessor
爲延遲初始化配置 s。若是沒有其餘bean引用aBean(Factory)PostProcessor
,則該後處理器根本不會被實例化。所以,將其標記爲延遲初始化將被忽略,Bean(Factory)PostProcessor
會急切地實例化,即便你設定的default-lazy-init
屬性true
對你的聲明<beans />
元素。
PropertyPlaceholderConfigurer
您可使用PropertyPlaceholderConfigurer
標準Java Properties
格式在單獨的文件中使用bean定義中的外部化屬性值。這樣作可使部署應用程序的人員自定義特定於環境的屬性,例如數據庫URL和密碼,而不會出現修改主XML定義文件或容器文件的複雜性或風險。
請考慮如下基於XML的配置元數據片斷,其中DataSource
定義了佔位符值:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/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>
複製代碼
該示例顯示了從外部Properties
文件配置的屬性。在運行時,a PropertyPlaceholderConfigurer
將應用於替換DataSource的某些屬性的元數據。要替換的值被指定爲表單的佔位符${property-name}
,它遵循Ant和log4j以及JSP EL樣式。
實際值來自標準Java Properties
格式的另外一個文件:
jdbc.driverClassName = org.hsqldb.jdbcDriver
jdbc.url = JDBC:HSQLDB:HSQL://localhost:9002
jdbc.username = root
jdbc.password =root
複製代碼
所以,${jdbc.username}
在運行時使用值「sa」替換字符串,這一樣適用於與屬性文件中的鍵匹配的其餘佔位符值。在PropertyPlaceholderConfigurer
爲大多數屬性和bean定義的屬性佔位符檢查。此外,您能夠自定義佔位符前綴和後綴。
使用context
Spring 2.5中引入的命名空間,您可使用專用配置元素配置屬性佔位符。您能夠在location
屬性中提供一個或多個位置做爲逗號分隔列表,如如下示例所示:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
複製代碼
在PropertyPlaceholderConfigurer
不只將查找在屬性Properties
指定的文件。默認狀況下,若是它在指定的屬性文件中找不到屬性,它還會檢查Java System
屬性。您能夠經過systemPropertiesMode
使用如下三個受支持的整數值之一設置configurer 的屬性來自定義此行爲:
never
(0):從不檢查系統屬性。fallback
(1):若是在指定的屬性文件中沒法解析,則檢查系統屬性。這是默認值。override
(2):在嘗試指定的屬性文件以前,首先檢查系統屬性。這使系統屬性能夠覆蓋任何其餘屬性源。有關PropertyPlaceholderConfigurer
更多信息,請參閱javadoc。
您可使用
PropertyPlaceholderConfigurer
替換類名稱,這在您必須在運行時選擇特定實現類時有時頗有用。如下示例顯示瞭如何執行此操做:<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:com/something/strategy.properties</value> </property> <property name="properties"> <value>custom.strategy.class=com.something.DefaultStrategy</value> </property> </bean> <bean id="serviceStrategy" class="${custom.strategy.class}"/><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:com/something/strategy.properties</value> </property> <property name="properties"> <value>custom.strategy.class=com.something.DefaultStrategy</value> </property> </bean> <bean id="serviceStrategy" class="${custom.strategy.class}"/> 複製代碼
若是類不能在分辨率,當它即將被創造,這是在失敗
preInstantiateSingletons()
的階段ApplicationContext
對非延遲實例化的bean。
PropertyOverrideConfigurer
在PropertyOverrideConfigurer
另外一個bean工廠後置處理器,相似 PropertyPlaceholderConfigurer
,但不一樣的是後者,原來的定義能夠有缺省值或者根本沒有值的bean屬性。若是覆蓋 Properties
文件沒有某個bean屬性的條目,則使用默認上下文定義。
請注意,bean定義不知道被覆蓋,所以從XML定義文件中能夠當即看出正在使用覆蓋配置器。若是多個PropertyOverrideConfigurer
實例爲同一個bean屬性定義了不一樣的值,則因爲覆蓋機制,最後一個實例會獲勝。
屬性文件配置行採用如下格式:
beanName.property=value
複製代碼
如下清單顯示了格式的示例:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
複製代碼
此示例文件能夠與包含名爲dataSource
has has driver
和url
properties 的bean的容器定義一塊兒使用 。
也支持複合屬性名稱,只要路徑的每一個組件(重寫的最終屬性除外)都已經非空(可能由構造函數初始化)。在下面的例子中,sammy
所述的屬性bob
的財產fred
的財產tom
豆被設置爲標量值123
:
tom.fred.bob.sammy = 123
複製代碼
指定的覆蓋值始終是文字值。它們不會被翻譯成bean引用。當XML bean定義中的原始值指定bean引用時,此約定也適用。
使用context
Spring 2.5中引入的命名空間,可使用專用配置元素配置屬性覆蓋,如如下示例所示:
<context:property-override location="classpath:override.properties"/>
複製代碼
FactoryBean
您能夠org.springframework.beans.factory.FactoryBean
爲本身工廠的對象實現接口。
該FactoryBean
接口是Spring IoC容器實例化邏輯的可插拔點。若是你有一個複雜的初始化代碼,用Java表示,而不是(可能)冗長的XML,你能夠建立本身的 FactoryBean
,在該類中編寫複雜的初始化,而後將自定義FactoryBean
插入容器。
該FactoryBean
接口提供了三種方法:
Object getObject()
:返回此工廠建立的對象的實例。能夠共享實例,具體取決於此工廠是返回單例仍是原型。boolean isSingleton()
:true
若是FactoryBean
返回單例或false
其餘方式返回 。Class getObjectType()
:返回getObject()
方法返回的對象類型,或者null
若是事先不知道類型。該FactoryBean
概念和接口被一些Spring框架內的場所。超過50個FactoryBean
接口的實現隨Spring一塊兒提供。
當你須要向一個容器詢問一個實際的FactoryBean
實例自己而不是它生成的bean 時,在調用the的方法時id
,用strersand符號(&
)做爲前綴。所以,對於給定 與的,調用在容器上返回的產品,而調用返回的 實例自己。getBean()``ApplicationContext``FactoryBean``id``myBean``getBean("myBean")``FactoryBean``getBean("&myBean")``FactoryBean