Spring官方文檔通讀-部分二

1.7 Bean定義繼承

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不然應用程序上下文將實際(嘗試)預先實例化abstractbean。編程

1.8 集裝箱擴建點

一般,應用程序開發人員不須要子類化ApplicationContext 實現類。相反,能夠經過插入特殊集成接口的實現來擴展Spring IoC容器。接下來的幾節將介紹這些集成接口。api

1.8.1 使用定製Bean BeanPostProcessor

BeanPostProcessor接口定義了您能夠實現的回調方法,以提供您本身的(或覆蓋容器的默認)實例化邏輯,依賴關係解析邏輯等。若是要在Spring容器完成實例化,配置和初始化bean以後實現某些自定義邏輯,則能夠插入一個或多個自定義BeanPostProcessor實現。

您能夠配置多個BeanPostProcessor實例,而且能夠BeanPostProcessor經過設置order屬性來控制這些實例的執行順序。只有在BeanPostProcessor實現Ordered 接口時才能設置此屬性。若是你本身編寫BeanPostProcessor,你也應該考慮實現這個Ordered接口。有關更多詳細信息,請參閱BeanPostProcessorOrdered接口的javadoc 。另見關於實例的程序化登記BeanPostProcessor的說明。

BeanPostProcessor實例在bean(或對象)實例上運行。也就是說,Spring IoC容器實例化一個bean實例,而後BeanPostProcessor實例執行它們的工做。BeanPostProcessor實例的範圍是每一個容器。僅當您使用容器層次結構時,這纔是相關的。若是BeanPostProcessor在一個容器中定義一個容器,它只會對該容器中的bean進行後處理。換句話說,BeanPostProcessor即便兩個容器都是同一層次結構的一部分,在一個容器中定義的bean也不會被另外一個容器中定義的bean進行後處理。要更改實際的bean定義(即定義bean的藍圖),您須要使用aBeanFactoryPostProcessor,如 使用。[定製配置元數據中所述`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

示例:Hello World,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屬性實際上(配置爲)依賴注入值。

1.8.2使用自定義配置元數據BeanFactoryPostProcessor

咱們看到的下一個擴展點是 org.springframework.beans.factory.config.BeanFactoryPostProcessor。這個接口的語義相似於BeanPostProcessor它的一個主要區別:BeanFactoryPostProcessor操做bean配置元數據。也就是說,Spring IoC容器容許BeanFactoryPostProcessor讀取配置元數據,並可能在容器實例化除實例以外的任何bean 以前更改它BeanFactoryPostProcessor

您能夠配置多個BeanFactoryPostProcessor實例,而且能夠BeanFactoryPostProcessor經過設置order屬性來控制這些實例的運行順序。可是,若是BeanFactoryPostProcessor實現 Ordered接口,則只能設置此屬性。若是你本身編寫BeanFactoryPostProcessor,你也應該考慮實現這個Ordered接口。有關更多詳細信息,請參閱BeanFactoryPostProcessorOrdered接口的javadoc 。

若是要更改實際的bean實例(即,從配置元數據建立的對象),則須要使用a BeanPostProcessor (前面在使用a定製Bean中進行了描述BeanPostProcessor)。雖然技術上能夠在a中使用bean實例BeanFactoryPostProcessor(例如,經過使用 BeanFactory.getBean()),但這樣作會致使過早的bean實例化,從而違反標準的容器生命週期。這可能會致使負面影響,例如繞過bean後期處理。此外,BeanFactoryPostProcessor實例的範圍是每一個容器的範圍。僅當您使用容器層次結構時,這纔有意義。若是BeanFactoryPostProcessor在一個容器中定義一個容器,則它僅應用於該容器中的bean定義。BeanFactoryPostProcessor即便兩個容器都是同一層次結構的一部分,一個容器中的Bean定義也不會被另外一個容器中的實例進行後處理。

Bean工廠後處理器在其內部聲明時會自動執行 ApplicationContext,以便將更改應用於定義容器的配置元數據。Spring包含許多預約義的bean工廠後處理器,例如PropertyOverrideConfigurerPropertyPlaceholderConfigurer。您還可使用自定義BeanFactoryPostProcessor - 例如,註冊自定義屬性編輯器。

一個ApplicationContext自動檢測部署在它實現了任何豆BeanFactoryPostProcessor接口。它在適當的時候使用這些bean做爲bean工廠後處理器。您能夠像處理任何其餘bean同樣部署這些後處理器bean。

BeanPostProcessors同樣,您一般不但願BeanFactoryPostProcessor爲延遲初始化配置 s。若是沒有其餘bean引用a Bean(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定義的屬性佔位符檢查。此外,您能夠自定義佔位符前綴和後綴。

使用contextSpring 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
複製代碼

此示例文件能夠與包含名爲dataSourcehas has driverurlproperties 的bean的容器定義一塊兒使用 。

也支持複合屬性名稱,只要路徑的每一個組件(重寫的最終屬性除外)都已經非空(可能由構造函數初始化)。在下面的例子中,sammy所述的屬性bob的財產fred的財產tom豆被設置爲標量值123

tom.fred.bob.sammy = 123
複製代碼

指定的覆蓋值始終是文字值。它們不會被翻譯成bean引用。當XML bean定義中的原始值指定bean引用時,此約定也適用。

使用contextSpring 2.5中引入的命名空間,可使用專用配置元素配置屬性覆蓋,如如下示例所示:

<context:property-override location="classpath:override.properties"/>
複製代碼

1.8.3使用自定義實例化邏輯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

相關文章
相關標籤/搜索