您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關BeanDefinition繼承與容器拓展點是什么,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
bean
定義包含一些配置信息,包括構造函數參數、屬性值、和容器特定信息,例如初始化方法、靜態工廠方法名等等。子bean
定義繼承父bean
定義配置數據。子bean
定義能夠覆蓋一些值或者增加其他需要。使用父bean
和子bean
定義能夠保存一些類型。實際上,這是一種模版模式。
如果你編程式使用ApplicationContext
接口,子bean
定義通過ChildBeanDefinition
類描述。大多數用戶不在這個級別使用(備注:應用開發人員一般不會接觸)。相反,它們在例如ClassPathXmlApplicationContext
之類的類中聲明性地配置bean
定義。當你使用基于XML配置元數據,你可以通過使用parent
屬性指示一個子bean
的定義,指定parent
bean作為這個屬性的值。下面例子顯示怎樣做:
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <!--parent指定繼承父類--> <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>
如果沒有指定,子bean
定義將使用父定義中的bean
類,但也可以覆蓋它。在后面這種情況下,子bean
類必須兼容父bean
定義(也就是說,必須接受父bean
的屬性值)。
子bean
定義繼承作用域、構造參數值、屬性值、和覆蓋父類方法,并可以添加新值。任何作用域、初始化方法、銷毀方法或static
工廠方法設置都會覆蓋對應的父bean
設置。
其余設置始終從子bean
定義中獲取:依賴、自動裝配、依賴檢查、單例和懶加載。
前面的例子通過使用abstract
屬性顯示標記父bean
定義作用一個抽象,如果父bean
定義沒有指定類,顯示地標記父bean
定義為abstract
是需要的,像下面例子展示:
<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
定義的模版且父bean
定義為子bean
定義服務。嘗試自己使用這樣的抽象父bean
,通過將其引用為另一個bean
的ref
屬性或使用父bean
ID
進行顯式的getBean()
調用將返回錯誤。類似地,容器的內部preInstantiateSingletons()
方法將忽略定義為抽象的bean定義。
默認情況下,
ApplicationContext
會預先實例化所有單例。因此,重要的是(至少對于單例bean),如果有一個(父)bean
定義僅打算用作模板,并且此定義指定了一個類,則必須確保將abstract
屬性設置為true
,否則應用程序上下文將實際上(試圖)預先實例化抽象Bean
。
參考實例:
com.liyong.ioccontainer.starter.XmlBeanDefinitionInheritanceContainer
典型地,應用程序開發者不需要實現ApplicationContext
類。相反,Spring IoC
容器通過插件實現指定的集成接口去擴展。下面的章節描述這些接口的集成。
BeanPostProcessor
自定義bean
BeanPostProcessor
接口定義回調方法,你可以實現這個接口提供你自己的(或者覆蓋容器的默認設置)初始化邏輯、依賴解析邏輯等等。如果你想實現一些自定義邏輯,在Spring
容器完成實例化、配置、初始化bean
之后,你可以插入一個或多個自定義BeanPostProcessor
實現。
你可以配置多個BeanPostProcessor
實例并且你可以通過設置order
屬性來控制這些BeanPostProcessor
實例的執行順序。僅僅BeanPostProcessor
實現Ordered
接口是可以設置這個屬性。如果自己實現BeanPostProcessor
,你應該考慮實現Ordered
接口。更多詳情,查看 BeanPostProcessor
和Ordered
接口。參見有關以編程方式注冊BeanPostProcessor實例的說明。
BeanPostProcessor
接口在bean
(對象)實例上操作。也就是說,Spring IoC
容器實例化一個bean
實例,然后BeanPostProcessor
實例執行它們的工作。(備注:在Spring容器初始化bean過程中執行相關的回調操作)
BeanPostProcessor
實例是按容器劃分作用域。僅僅在使用繼承容器才有意義。如果在一個容器中定義BeanPostProcessor
,僅僅在那個容器中的bean
被后置處理。換句話說,定義在一個容器中的bean
不會被在其他容器定義的BeanPostProcessor
所處理,即使兩個容器都屬于同一層次結構。改變實際
bean
定義(也就是定義bean
的藍圖),你需要使用BeanFactoryPostProcessor
,如使用BeanFactoryPostProcessor自定義配置元數據中所述。
org.springframework.beans.factory.config.BeanPostProcessor
接口恰好地由兩個回調方法組成。當一個類作為后置處理起被注冊到容器中時,對于每個被容器創建bean
實例,后置處理器從容器初始化方法(例如:InitializingBean.afterPropertiesSet()
或者任何被聲明init方法)被調用之前,并且任何bean
初始化回調之后獲得回調。后置處理器能夠處理bean實例任何操作,包括忽略所有的回調。Bean
后處理器通常檢查回調接口,或者可以用代理包裝Bean
。Spring AOP
基礎設施類中實現bean
的后置處理去提供一個代理包裝邏輯。
ApplicationContext
自動的檢查所有bean
,這些bean
在配置元數據中實現了BeanPostProcessor
接口。ApplicationContext
注冊這些bean
作為后置處理器,以便以后在創建bean
時可以調用它們。Bean
后處理器可以與其他bean
相同的方式部署在容器中。
注意,當通過在類上使用@Bean
工廠方法聲明BeanPostProcessor
時,工廠方法返回類型應該是實現類本身或只是實現org.springframework.beans.factory.config.BeanPostProcessor
接口,清晰地表明該bean
的后處理器性質。否則,ApplicationContext
無法在完全創建之前按類型自動檢測它。由于BeanPostProcessor
需要及早實例化才能應用于上下文中其他bean
的初始化,因此這種早期類型檢測至關重要。
編程式地注冊BeanPostProcessor實例
推薦的去注冊
BeanPostProcessor
方式是通過ApplicationContext
自動檢測(前面描述),可以使用addBeanPostProcessor
方法以編程方式針對ConfigurableBeanFactory
注冊它們。當注冊之前你需要評估條件邏輯甚至需要跨層次結構的上下文復制后置處理器時,這是非常有用的。注意,然而,BeanPostProcessor
實例編程式的添加不遵循Ordered
排序接口。在這里,注冊的順序決定了執行的順序。<u>需要注意是編程式的注冊BeanPostProcessor
實例總是在這些通過自動檢測的后置處理器之后被處理,而不管顯示的順序</u>。
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)
.如果你有通過使用自動裝配或
@Resource
注入bean
到BeanPostProcessor
中(可能回退到自動裝配),當搜索類型匹配的依賴項候選者時,Spring
可能會訪問意外的Bean,因此使它們不適合進行自動代理或其他類型的Bean后處理。例如,如果你有一個用@Resource
標注的依賴項,其中字段或Setter
名稱不直接與bean
的聲明名稱相對應,并且不使用name
屬性,那么Spring
將訪問其他bean
以按類型匹配它們。
下面的例子展示了在ApplicationContext
中怎樣去編寫、注冊和使用BeanPostProcessor
接口。
例子:Hello World
第一個例子說明基礎的使用。這個例子展示一個自定義BeanPostProcessor
實現并調用通過容器創建的每個bean
的toString()
方法并且打印結果字符串到控制臺。
下面的清單展示了自定義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) { //打印bean信息 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 = ctx.getBean("messenger", Messenger.class); System.out.println(messenger); } }
前面的應用輸出結果類似下面:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961
例子:RequiredAnnotationBeanPostProcessor
將回調接口或與自定義BeanPostProcessor
實現結合使用是擴展Spring IoC
容器常用方法。Spring
的RequiredAnnotationBeanPostProcessor
是一個示例,它是Spring
發行版附帶的BeanPostProcessor
實現,可以確保標有(任意)注解的bean
上的JavaBean
屬性實際上(配置為)依賴注入了一個值。說明:就是常用的依賴注入。
參考代碼:com.liyong.ioccontainer.starter.XmlBeanPostProcessorIocContainer
下一個拓展點是org.springframework.beans.factory.config.BeanFactoryPostProcessor
。這個接口語義類似BeanPostProcessor,一個重要的不同點:BeanFactoryPostProcessor
是操作在bean的配置元數據上。也就是說,Spring IoC容器允許除BeanFactoryPostProcessor
實例外其他任何bean被BeanFactoryPostProcessor
讀取配置元數據和改變它。
你可以配置多個BeanFactoryPostProcessor
實例,并且你可以通過設置order屬性在這些BeanFactoryPostProcessor
實例上來控制順序。然而,如果BeanFactoryPostProcessor
實現Ordered
接口才能設置這個屬性。如果你寫自己的BeanFactoryPostProcessor
,你應該考慮實現Ordered
接口。更多關于BeanFactoryPostProcessor
和 Ordered
接口詳細信息。
如果想去改變實際bean實例(也就是,這個對象是從配置元數據被創建的),你需要使用
BeanPostProcessor
(前面描述通過使用BeanPostProcessor
自定義bean)。從技術上講,可以在BeanFactoryPostProcessor
中使用Bean實例(例如,通過使用BeanFactory.getBean()
),這樣做導致過早的初始化bean,違反了標準容器生命周期。這會導致負面的影響,例如:繞過后置處理。同樣,
BeanFactoryPostProcessor
實例是按容器劃分。(如果你使用分層的容器才會有意義) <u>如果你定義在容器中定義一個BeanFactoryPostProcessor
,它僅適用于該容器中的bean定義。在一個容器中的bean定義不會被其他的容器中BeanFactoryPostProcessor
后置處理,即使兩個容器在同一個層級</u>。
Bean工廠后處理器在ApplicationContext
中聲明時會自動執行,以便將更改應用于定義容器的配置元數據。Spring包括一些預定義的工廠后置處理器,例如PropertyOverrideConfigurer
和
PropertySourcesPlaceholderConfigurer
。你可以使用一個自定義的BeanFactoryPostProcessor
-例如,注冊一個自定義屬性編輯器。
ApplicationContext
自動地檢測任何部署到容器中并實現BeanFactoryPostProcessor
接口的實例bean。使用這些bean作為bean工廠后置處理器,在適當的時間。你可以像其他ban一樣部署這些后置處理器。
與
BeanPostProcessors
一樣,通常不希望配置BeanFactoryPostProcessors
進行延遲初始。如果沒有其他bean引用Bean(Factory)PostProcessor
,這個后置處理器不會被初始化。因此,標記它為延遲初始化將被忽略并且Bean(Factory)PostProcessor
將被提前初始化即使你的<beans />聲明default-lazy-init
屬性為true。
參考代碼:
com.liyong.ioccontainer.starter.XmlBeanFactoryPostProcessorIocContainer
例如:類名替換PropertySourcesPlaceholderConfigurer
可以使用PropertySourcesPlaceholderConfigurer
通過使用標準Java屬性格式將屬性值從bean定義外部化到一個單獨的文件中(意思是:bean配置數據可以配置到一個單獨文件中)。這樣做使部署應用程序的人員可以自定義特定于環境的屬性,例如數據庫URL和密碼,而無需為修改容器的主要XML定義文件而復雜或冒風險。
考慮下面基于XML配置元數據片段,DataSource
使用占位符值定義:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <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
文件的屬性配置。在運行時,PropertySourcesPlaceholderConfigurer
應用元數據替換DataSource
的屬性值。將要替換的值指定為$ {property-name}
格式的占位符,該格式遵循Ant
、log4j
和JSPEL
樣式。
真實值來自于標準的Properties
格式的其他文件:
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
因此,${jdbc.username}
字符串在運行時被替換為sa
值,并且同樣應用到其他的占位符在properties
文件中匹配這些key。PropertySourcesPlaceholderConfigurer
檢查Bean定義的大多數屬性和屬性中的占位符。此外,你可以自定義占位符的前綴和后綴。
context命名空間在Spring2.5中被引入,你可以配置一個專用配置元素的占位符。在location
屬性中你可以提供一個或多個location
通過逗號分隔,類似下面例子:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
PropertySourcesPlaceholderConfigurer
不僅僅在你指定的Properties中查找屬性。默認情況,如果在指定的屬性文件中沒有找到屬性,它會再次檢查Spring Environment
屬性和常規的Java System屬性。
你可以使用
PropertySourcesPlaceholderConfigurer
去替代類名稱,有時候者非常有用,當你在運行時必須選擇一個特定的實現類。下面例子展示怎樣去做:<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer"> <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時,即在非
lazy-init
bean的ApplicationContext
的preInstantiateSingletons()
階段,bean的解析將失敗。
參考代碼:
com.liyong.ioccontainer.starter.XmlPropertySourcesPlaceholderConfigurerIocContainer
例子:PropertyOverrideConfigurer
另一個bean工廠后處理器PropertyOverrideConfigurer
類似于PropertySourcesPlaceholderConfigurer
,但與后者不同,原始定義可以具有bean屬性的默認值,也可以完全沒有值。如果覆蓋的屬性文件對于一些bean屬性沒有符合內容,默認的上下文定義被使用。
注意:bean定義沒有意識到被覆蓋,因此從XML定義文件中不能立即看出覆蓋的配置正在被使用。由于存在覆蓋機制,在多個PropertyOverrideConfigurer
實例情況下對應相同bean屬性不同的值,最后一個將被使用。
屬性文件配置格式:
beanName.property=value
下面清單顯示格式化例子:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
此示例文件可與容器定義一起使用,容器定義包含一個名為dataSource
的bean,該bean具有driver
和url
屬性。
復合屬性名也是被支持的,只要路徑的每個組件(被覆蓋的最終屬性除外)都是非空的(可能是由構造函數初始化的)。下面的例子,bean的tom
屬性fred
的sammy
屬性設置只123:
tom.fred.bob.sammy=123
指定的重寫值總是文本值。它們沒有被轉譯為bean引用。當XML bean定義中的原始值指定bean引用時,這種約定也適用。
context命名空間在Spring2.5中被引入,可以使用專用配置元素配置屬性覆蓋,類似下面例子:
<context:property-override location="classpath:override.properties"/>
參考代碼:
com.liyong.ioccontainer.starter.XmlPropertyOverrideConfigurerIocContainer
可以為本身就是工廠的對象實現org.springframework.beans.factory.FactoryBean
接口。
這個FactoryBean
接口是Spring IoC容器的實例化邏輯可插拔點。如果你有一個復雜的初始化代碼在Java中比冗余XML更好的表達,你可以創建你自己的FactoryBean
,在實現類中寫復雜的初始化邏輯,然后插入你的自定義FactoryBean
到容器中。
FactoryBean
接口提供三個方法:
Object getObject()
:返回這個工廠創建的一個實例。這個實例可能被共享,依賴于這個工廠返回的是單例或原型。
boolean isSingleton()
:如果FactoryBean
返回一個單例返回true否則為false
Class getObjectType()
:返回由getObject()方法返回的對象類型;如果類型未知,則返回null
FactoryBean
概念和接口在Spring框架中一些地方被使用到。Spring有超過50個FactoryBean
接口的實現。
當你需要向容器獲取一個實際的FactoryBean
實例本身而不是由它產生的bean時請在調用ApplicationContext的getBean()
方法時在該bean的ID前面加上一個&
符號。因此,對于id為myBean
的給定FactoryBean
,在容器上調用getBean(“myBean”)
將返回FactoryBean
的產生的bean,而調用getBean(“&myBean”)
將返回FactoryBean
實例本身。
關于BeanDefinition繼承與容器拓展點是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。