注意LifecycleProcessor
接口繼承了Lifcycle
接口狮鸭。同時善玫,增加了2個方法洒敏,用于處理容器的refreshed
和closed
事件。
startup
和shutdown
方法調(diào)用次序非常重要拿愧。若兩個對象有依賴關(guān)系,依賴方會在依賴啟動之后啟動,會在依賴停止之前停止。然而,有時依賴并不直接碌尔。也許你僅知道某些類型對象優(yōu)先于另外一種類型啟動赶掖。此場景中,SmartLifecycle
接口也許是個好主意,該接口有個方法getPhase()
,此方法是其父接口Phased
中的方法:
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
啟動時七扰,最低層次的phase
最先啟動奢赂,停止時,該次序逆序執(zhí)行颈走。因此膳灶,若對象實(shí)現(xiàn)了SmartLifecycle
接口,它的getPhase()
方法返回Integer.MIN_VALUE
立由,那么該對象最先啟動轧钓,最后停止。若是返回了Integer.MAX_VALUE
锐膜,那么該方法最后啟動最先停止(因?yàn)樵搶ο笠蕾嚻渌鸼ean才能運(yùn)行)毕箍。關(guān)于phase
的值,常規(guī)的并未實(shí)現(xiàn)SmartLifecycle
接口的Lifecycle
對象道盏,其值默認(rèn)為0而柑。因此文捶,負(fù)phase
值表示要在常規(guī)Lifecycle
對象之前啟動(在常規(guī)Lifecycyle
對象之后停止),使用 正值則恰恰相反媒咳。
如你所見粹排,SmartLifecycle
中stop()
方法有一個回調(diào)參數(shù)。所有的實(shí)現(xiàn)在關(guān)閉處理完成后會調(diào)用回調(diào)的run()
方法涩澡。TODO 顽耳。它相當(dāng)于開啟了異步關(guān)閉功能,和LifecycleProcessor
接口默認(rèn)實(shí)現(xiàn)DefaultLifecycleProcessor
類的異步妙同,該類會為每個phase
的回調(diào)等待超時射富。每個phase
默認(rèn)的超時是30秒≈嘀悖可以重寫該類默認(rèn)的實(shí)例胰耗,該類在容器內(nèi)默認(rèn)bean名稱是lifecycleProcessor
。如果你僅想修改超時茎辐,這么寫就足夠了宪郊。
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
As mentioned, the LifecycleProcessor interface defines callback methods for the refreshing and closing of the context as well. The latter will simply drive the shutdown process as if stop() had been called explicitly, but it will happen when the context is closing. The refresh callback on the other hand enables another feature of SmartLifecycle beans. When the context is refreshed (after all objects have been instantiated and initialized), that callback will be invoked, and at that point the default lifecycle processor will check the boolean value returned by each SmartLifecycle object’s isAutoStartup() method. If "true", then that object will be started at that point rather than waiting for an explicit invocation of the context’s or its own start() method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The "phase" value as well as any "depends-on" relationships will determine the startup order in the same way as described above.
TODO 書接前文,LifecycleProcessor
接口也定義了容器的refreshing
和closing
事件拖陆。后者會驅(qū)動shutdown
處理弛槐,就像是明確的調(diào)用了stop()
方法,但是它是發(fā)生在容器關(guān)閉期間。refresh
回調(diào)開啟了SmartLifecycle
bean的另一個功能 依啰。當(dāng)上下文環(huán)境刷新時(在所有的對象實(shí)例化和初始化之后),則會調(diào)用refresh回調(diào)乎串,同時,默認(rèn)的lifecycle processor
檢查每個SmartLifecycle
對象的isAutoStartup()
方法返回的布爾值速警。若為true
,對象則會在那時啟動叹誉,而不是等待容器顯示調(diào)用之后或者是他自己的start()
方法調(diào)用之后(這和容器刷新不同,標(biāo)準(zhǔn)的容器實(shí)現(xiàn)啟動不會自動發(fā)生)闷旧。phase
值和depends-on
關(guān)系一樣长豁,都使用了相同的方法決定了的啟動次序。
<h5 id='beans-factory-shutdown'>非web應(yīng)用中安全的關(guān)閉Spring IoC容器</h5>
本章適用于非web應(yīng)用忙灼〗辰螅基于Spring web的應(yīng)用的
ApplicationContext
實(shí)現(xiàn)類,已經(jīng)提供了支持,用于在應(yīng)用關(guān)閉時安全的關(guān)閉Spring IoC容器。
在一個非web應(yīng)用的環(huán)境中使用Spring IoC容器;比如,在一個富客戶端桌面的環(huán)境中亲雪;得在JVM中注冊一個shutdown
鉤子。這么做是為了安全的關(guān)閉啃勉,在關(guān)閉時保證所單例bean的相關(guān)的destroy
方法會被調(diào)用,這樣就可以釋放所有的資源双妨。當(dāng)然了淮阐,你必須得正確的配置和實(shí)現(xiàn)銷毀回調(diào)叮阅。
要注冊shutdown鉤子,得調(diào)用registerShutdownHood()
方法枝嘶,該方法在AbstractApplicationContext
類中帘饶。
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
new String []{"beans.xml"});
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
<h4 id='beans-factory-aware'>ApplicationContextAware and BeanNameAware</h4>
org.springframework.context.ApplicationContextAware
接口實(shí)現(xiàn)類的實(shí)例將會持有ApplicationContext
的引用:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此可以編程式的使用ApplicationContext
手動的創(chuàng)建bean,通過ApplicationContext
接口或者是該接口的子類哑诊,比如ConfigurableApplicationContext
群扶,該類還增加了方法。用途之一是編程式的檢索bean镀裤,有時非常有用竞阐。然而,大多數(shù)情況下暑劝,要避免編程式檢索bean骆莹,這樣的話你的代碼就會和Spring耦合,這不是IoC的風(fēng)格担猛,Ioc的風(fēng)格是協(xié)作類作為bean的屬性幕垦。ApplicationContext
類的其他方法提供了文件資源的訪問接口、發(fā)布應(yīng)用事件傅联、訪問MessageSource
消息資源先改。這些附加的功能請參看Section 5.15, “Additional Capabilities of the ApplicationContext”
自Spring2.5起,可以使用自動裝配獲取ApplicationContext
引用蒸走。傳統(tǒng)的constructor
和byType
自動裝配模式(詳情參看 Section 5.4.5, “Autowiring collaborators”能為構(gòu)造參數(shù)或者setter
方法提供一個ApplicationContext
類的依賴注入仇奶。為了更加靈活,還增加了自動注入的注解功能比驻,它能自動注入屬性和自動注入多參數(shù)方法该溯。使用注解,ApplicationContext
可以自動注入到ApplicationContext
類型的屬性别惦、構(gòu)造參數(shù)狈茉、方法參數(shù)。詳情參看Section 5.9.2, “@Autowired”.
org.springframework.beans.factory.BeanNameAware
接口的實(shí)現(xiàn)類掸掸,若是由ApplicationContext
創(chuàng)建了該類的實(shí)例,該實(shí)例將會持有相關(guān)的對象定義的引用氯庆。
public interface BeanNameAware {
void setBeanName(string name) throws BeansException;
}
The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean afterPropertiesSet or a custom init-method.
TODO這個回調(diào)在設(shè)置屬性之后調(diào)用,但是在initialization
回調(diào)之前猾漫,比如InitializingBean
的afterPropertiesSet
或者 自定義的init-method
<h4 id='aware-list'>Other Aware interfaces</h4>
Besides ApplicationContextAware and BeanNameAware discussed above, Spring offers a range of Aware interfaces that allow beans to indicate to the container that they require a certain infrastructure dependency. The most important Aware interfaces are summarized below - as a general rule, the name is a good indication of the dependency type:
名稱 | 注入依賴 | 詳情 |
---|---|---|
ApplicationContextAware | ApplicationContext | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
ApplicationEventPublisherAware | 發(fā)布事件 | Section 5.15, “Additional Capabilities of the ApplicationContext” |
BeanClassLoaderAware | 加載bean的類加載器 | Section 5.3.2, “Instantiating beans” |
BeanFactoryAware | 聲明BeanFactory | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
BeanNameAware | 生命bean 的名字 | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
BootstrapContextAware | Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts | Chapter 26, JCA CCI |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time | Section 9.8.4, “Load-time weaving with AspectJ in the Spring Framework” |
MessageSourceAware | Configured strategy for resolving messages (with support for parametrization and internationalization) | Section 5.15, “Additional Capabilities of the ApplicationContext” |
NotificationPublisherAware | Spring JMX notification publisher | Section 25.7, “Notifications” |
PortletConfigAware | Current PortletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 20, Portlet MVC Framework |
PortletContextAware | Current PortletContext the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 20, Portlet MVC Framework |
ResourceLoaderAware | Configured loader for low-level access to resources | Chapter 6, Resources |
ServletConfigAware | Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 17, Web MVC framework |
除上面討論過的ApplicationContextAware
和BeanNameAware
点晴,Spring提供了一些了Aware
接口,這些接口可以提供容器中相關(guān)的基礎(chǔ)(SpringAPI)依賴悯周。最重要的Aware
接口參看下面的摘要粒督,命名相當(dāng)規(guī)范,看名字就能知道依賴類型:
Table 5.4. Aware interfaces
名稱 | 注入依賴 | 詳情 |
---|---|---|
ApplicationContextAware | ApplicationContext | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
ApplicationEventPublisherAware | 發(fā)布事件 | Section 5.15, “Additional Capabilities of the ApplicationContext” |
BeanClassLoaderAware | 加載bean的類加載器 | Section 5.3.2, “Instantiating beans” |
BeanFactoryAware | 聲明BeanFactory | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
BeanNameAware | 生命bean 的名字 | Section 5.6.2, “ApplicationContextAware and BeanNameAware” |
BootstrapContextAware | Resource adapter BootstrapContext the container runs in. Typically available only in JCA aware ApplicationContexts | Chapter 26, JCA CCI |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time | Section 9.8.4, “Load-time weaving with AspectJ in the Spring Framework” |
MessageSourceAware | Configured strategy for resolving messages (with support for parametrization and internationalization) | Section 5.15, “Additional Capabilities of the ApplicationContext” |
NotificationPublisherAware | Spring JMX notification publisher | Section 25.7, “Notifications” |
PortletConfigAware | Current PortletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 20, Portlet MVC Framework |
PortletContextAware | Current PortletContext the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 20, Portlet MVC Framework |
ResourceLoaderAware | Configured loader for low-level access to resources | Chapter 6, Resources |
ServletConfigAware | Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext | Chapter 17, Web MVC framework |
注意禽翼,這些接口的用法使代碼與Spring API耦合屠橄,這不符合IoC風(fēng)格族跛。同樣,除非有需求的基礎(chǔ)bean才使用編程式訪問容器锐墙。
<h3 id='beans-child-bean-definitions'>Spring Bean的繼承</h3>
Spring bean定義包含各種配置信息礁哄,包括構(gòu)造參數(shù),屬性值溪北,容器特定信息例如初始化方法桐绒、靜態(tài)工廠方法等等。Spring子bean定義繼承父bean定義配置之拨。子bean能覆蓋值茉继,若有需要還能增加其他配置。使用繼承能少打好多字蚀乔。這是模板的一種形式烁竭,講究的就是效率。
編程式的方式使用ApplicationContext
場景吉挣,子bean的定義代表ChildBeanDefinition
類派撕。大多數(shù)用戶不需要使用如此底層的SpringAPI,通常是使用類似ClassPathXmlApplicationContext
的bean聲明睬魂。若用XML配置终吼,通過parent
屬性表示子bean定義,指定父bean的標(biāo)識作為parent
屬性值汉买。
<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>
若子bean中未指定class
屬性衔峰,則子bean集成父bean的class
屬性,子bean可以重寫覆蓋此屬性蛙粘。若要覆蓋重寫class
屬性垫卤,子bean的class類型必須兼容父bean的class,也就是,子bean必須能接收父bean的屬性值出牧。
其他的屬性也是通常取自子bean的配置:depends on, autowire mode, dependency check, singleton, lazy init.
前面樣例中穴肘,使用abstract
屬性指定了父bean為抽象定義。如父bean中未指定class,則必須指定父bean為抽象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不能實(shí)例化,因?yàn)樗煌暾矗浅橄蟮腷ean慨代,作為子bean的純模板時,它是非常有用的啸如。試試通過屬性引用或者使用getBean()
方法調(diào)用該bean侍匙,會拋錯。容器內(nèi)部的preInstantiateSingletons()
方法會忽略抽象bean叮雳。
ApplicationContext
類默認(rèn)會預(yù)先實(shí)例化所有的單例bean想暗。因此妇汗,如果有做模板用的父bean,父bean定義中指定了classs
屬性,則必須指定abstract
為true
,這是非常重要的,否則容器會預(yù)先實(shí)例化該bean说莫。
<h3 id='beans-factory-extension'>容器擴(kuò)展點(diǎn)</h3>
通常開發(fā)者無需自己實(shí)現(xiàn)APplicationContext
杨箭,而是使用插件擴(kuò)展Spring IoC容器,插件是某些指定的集成接口的實(shí)現(xiàn)储狭。下面記賬講解這些集成接口互婿。
<h4 id='beans-factory-extension-bp'>使用BeanPostProcessor自定義bean</h4>
BeanPostProcessor
接口定義了實(shí)例化邏輯、依賴邏輯等回調(diào)方法,即可以自定義也可以覆蓋容器默認(rèn)方法晶密。若果要在Spring容器完成實(shí)例化擒悬、配置模她、初始化bean之后執(zhí)行自定義邏輯,則以插件方式實(shí)現(xiàn)BeanPostProcessor
稻艰。
可以配置多個BeanPostProcessor
實(shí)例,可以設(shè)置BeanPostProcessors
的order
屬性來控制其執(zhí)行次序侈净。讓BeanPostProcessor
實(shí)現(xiàn)Ordered
接口尊勿,就能設(shè)置次屬性。如果使用自定義BeanPostProcessor
畜侦,也得考慮實(shí)現(xiàn)Ordered
接口元扔。更多的細(xì)節(jié),參閱BeanPostProcessor
和Ordered
接口的javadocs旋膳。也可以查閱programmatic registration of BeanPostProcessors譯注澎语,SPring參考手冊中這個鏈接確實(shí)沒有
NOTE
BeanPostProcessors
操作bean的實(shí)例;也就是,Spring IoC容器實(shí)例化bean的實(shí)例時BeanPostProcessors
開始運(yùn)行验懊。
BeanPostProcessors
在各自容器內(nèi)有效擅羞。當(dāng)使用容器繼承時,BeanPostProcessors
缺不會繼承义图。如果在某容器內(nèi)定義了BeanPostProcessor
减俏,近在本容器中生效〖罟ぃ或句話說娃承,一個容器中的bean不會使用另一個容器內(nèi)的BeanPostProcessor
處理,繼承的容器也不行怕篷。要改變bean定義(也就是历筝,bean定義的藍(lán)圖,譯注藍(lán)圖應(yīng)該是指各種配置元數(shù)據(jù)廊谓,比如xml梳猪、注解等),你得使用
BeanFactorPostProcessor
,詳情參看in Section 5.8.2, “Customizing configuration metadata with a BeanFactoryPostProcessor”
org.springframework.beans.factory.config.BeanPostProcessor
接口有2個回調(diào)方法組成蹂析。當(dāng)這樣的類在容器內(nèi)注冊為post-processor
舔示,容器創(chuàng)建所有bean,在容器初始化方法(比如InitializingBean
的afterProperieSet()
方法和其他所有的聲明的init
方法)和所有bean 初始化回調(diào)之前碟婆,運(yùn)行post-processor
回調(diào)。
ApplicationContext
自動探測在配置元數(shù)據(jù)中定義的BeanPostProcessor
惕稻。ApplicationContext
注冊這些bean為post-processors
竖共,這樣就可以在bean創(chuàng)建之前調(diào)用。Bean的post-processors
可以像其他bean那樣部署到容器里俺祠。
注意公给,在configuration
類中,使用@Bean
工廠方法聲明BeanPostProcessor
蜘渣,該工廠方法的返回類型必須是該實(shí)現(xiàn)類或者至少得是org.springframework.beans.factory.config.BeanPostProcessor
接口淌铐,清楚的標(biāo)識出post-processor
。否則,ApplicationContext
不會開啟根據(jù)類型自動探測蔫缸。因?yàn)?code>BeanPostProcessor需要盡早的實(shí)例化腿准,這樣在容器中即可用于其他bean的初始化,因此這種盡早的類型探測至關(guān)重要。
注意
編程式注冊BeanPostProcessor
盡管推薦的BeanPostProcessor
的注冊方式是通過ApplicationContext
的自動探測機(jī)制,但是也可以使用ConfigurableBeanFactory
類調(diào)用其addBeanPostProcessor
實(shí)現(xiàn)編程式的注冊拾碌。編程式注冊是非常有用的吐葱,比如用于在注冊之前實(shí)現(xiàn)等價的邏輯,再比如跨容器復(fù)制post processors
校翔。注意使用編程式注冊BeanPostProcessors
并不會遵守Ordered
接口的次序弟跑。注冊的順序就是執(zhí)行的次序。此外還得記得防症,編程式的注冊BeanPostProcessors
會在自動探測注冊的BeanPostProcessors
之前處理,無論自動探測注冊的BeanPostProcessors
指定了多么優(yōu)先的次序孟辑。
注意
BeanPostProcessor和AOP的自動代理
Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessors are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessors nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.
容器會特殊對待BeanPostProcessor
接口。所有的BeanPostProcessors
及引用了BeanPostProcessors
的bean會在啟動時實(shí)例化蔫敲,作為ApplicationContext
特殊的啟動階段饲嗽。接下來,所有的BeanPostProcessors
都會按照次序注冊到容器中燕偶,在其他bean使用BeanPostProcessors
處理時也會使用此順序喝噪。因?yàn)锳OP的auto-proxying自動代理是BeanPostProcessor
的默認(rèn)實(shí)現(xiàn),它既不引用BeanPostProcessors
也不引用其他bean指么,不會發(fā)生auto-proxying自動代理,因此不會有切面織入酝惧。TODO對于
BeanPostProcessor
類型的bean,會看到這樣一條日志:"Bean foo is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)"
注意伯诬,如果有bean通過自動注入或者@Resource
(可能會導(dǎo)致自動注入)注入到BeanPostProcessor
,在使用類型匹配檢索依賴bean時Spring也許會訪問到不期望的bean晚唇,導(dǎo)致生成不合適的auto-proxying自動代理或者其他post-processing
。舉個栗子盗似,如果使用@Resouce
依賴注解哩陕,而且field/setter
上注解的名字和bean中聲明名字不一致時,Spring將會使用類型匹配訪問其他bean。
下面 示例中講解了在ApplicationContext
中如何撰寫、注冊悍及、使用BeanPostProcessors
栗子:Hello World,BeanPostProcessor風(fēng)格
第一個示例闽瓢,講解基礎(chǔ)用法。栗子展示了一個自定義BeanPostProcessor
實(shí)現(xiàn)心赶,功能是在容器創(chuàng)建bean時扣讼,調(diào)用每一個bean的toString()
方法并輸出到控制臺。
上干活缨叫,fuck goods
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://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
是如何定義的椭符。它甚至沒有名字,因?yàn)樗芟衿渌鸼ean那樣依賴注入耻姥。(上面的配置中销钝,使用Groovy script
創(chuàng)建了個bean。Spring動態(tài)語言支持的詳細(xì)講解參看Chapter 29, Dynamic language support
下面的java應(yīng)用使用上面的配置和代碼執(zhí)行,
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 messenger created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Example: The RequiredAnnotationBeanPostProcessor
對于擴(kuò)展Spring IoC容器琐簇,使用回調(diào)函數(shù)或者注解聯(lián)結(jié)一個自定義BeanPostProcessor
實(shí)現(xiàn)類是常用的手段蒸健。例如Spring的RequiredAnnotationBeanPostProcessor
,是個BeanPostProcessor
實(shí)現(xiàn)類鸽嫂,spring內(nèi)置纵装,作用是確保Spring bean定義上的帶注解的JavaBean屬性確實(shí)被注入了值。
<h4 id='beans-factory-extension-factory-postprocessors'>使用BeanFactoryPostProcessor自定義配置元數(shù)據(jù)</h4>
接下來的擴(kuò)展點(diǎn)講一講org.springframework.beans.factory.config.BeanFactoryPostProcessor
据某。此接口的語法和BeanPostProcessor
類似,有一個主要的不同之處:BeanFactoryPostProcessor
操作bean的配置元數(shù)據(jù);也就是诗箍,Spring IoC容器允許BeanFactoryPostProcessor
讀取配置元數(shù)據(jù)并且在容器實(shí)例化bean之前可能修改配置癣籽。
可以配置多個BeanFactoryPostProcessors
,通過設(shè)置order
屬性控制它們的執(zhí)行次序滤祖。BeanFactoryPostProcessor
若是實(shí)現(xiàn)了Ordered
接口筷狼,則可設(shè)置該屬性。若是自定義BeanFactorPostProcessor
匠童,同時得考慮實(shí)現(xiàn)Ordered
接口埂材。詳情參閱BeanFactoryPostProcessor
的javadocs。
NOTE
如果要改變bean實(shí)例(根據(jù)配置元數(shù)據(jù)創(chuàng)建的對象),那么就需要使用BeanPostProcessor
(上一章描述的in Section 5.8.1, “Customizing beans using a BeanPostProcessor”)汤求。當(dāng)使用BeanFactoryPostProcessor
處理實(shí)例時(使用BeanFactory.getBean()方法),如此早的處理bean實(shí)例俏险,違反了標(biāo)準(zhǔn)的容器生命周期。通過bean post processing
也許會引起負(fù)面影響扬绪。
BeanFactoryPostProcessors
的作用域也是在各自的容器內(nèi)竖独。如果使用容器繼承,這一點(diǎn)也是應(yīng)該注意的挤牛。如果在某容器內(nèi)定義了BeanFactoryPostProcessor
,則僅應(yīng)用于本容器莹痢。某容器內(nèi)的bean定義,不會使用另一個容器的BeanFactoryPostProcessors
處理,容器之間有繼承關(guān)系也不行竞膳。
為了讓配置元數(shù)據(jù)的改變應(yīng)用航瞭,聲明在ApplicationContext
內(nèi)的bean工廠post-processor
都是自動執(zhí)行。Spring包含一系列的預(yù)先定義的bean工廠post-processors
,比如PropertyOverrideConfigurer
和PropertyPlaceholderConfigurer
坦辟。也可以使用自定義BeanFactoryPostProcessor
沧奴,比如注冊一個自定義屬性編輯器。
ApplicationContext
自動探測BeanFactoryPostProcessor
接口的實(shí)現(xiàn)類长窄。容器使用這些bean作為bean工廠post-processors
滔吠。可以像其他bean那樣將post-processor
部署在容器內(nèi)挠日。
NOTE
若使用BeanPostProcessors
,通常不會給BeanFactoryPostProcessors
配置延遲初始化疮绷。如果沒有其他bean引用BeanFactoryPostProcessor
,則post-processor
根本不會實(shí)例化。因此設(shè)置延遲初始化將會被忽略嚣潜,BeanFactoryPostProcessor
將會及時實(shí)例化冬骚,甚至在<beans/>
元素設(shè)置了default-lazy-init
屬性為true
也不行。
<h5 id='beans-factory-placeholderconfigurer'>Example: the Class name substitution PropertyPlaceholderConfigurer</h5>
可以使用PropertyPlaceholderConfigurer
將bean的屬性值使用標(biāo)準(zhǔn)的Java Properties格式定義在一個單獨(dú)的文件中懂算。這樣可以將應(yīng)用的自定義環(huán)境配置屬性隔離出來只冻,比如數(shù)據(jù)庫URLs和密碼,這樣就降低了修改容器內(nèi)XML配置或者Java 代碼的的復(fù)雜性和風(fēng)險计技。
考慮下面的XML配置片段喜德,使用了placeholder
值定義了DataSource
。樣例展示了一個外部的Properties
文件的屬性配置垮媒。運(yùn)行時舍悯,PropertyPlaceholderConfigurer
會應(yīng)用到配置元數(shù)據(jù)中,替換指定格式的placeholders
,格式為${property-name}
睡雇,這樣的格式與Ant/log4j/JSP EL
風(fēng)格相同萌衬。
<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>
在標(biāo)準(zhǔn)java Properties格式文件中實(shí)際的值:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
因此,字串${jdbc.username}
在運(yùn)行時賦值為sa
它抱,其他的${key}
都會被替換為文件中與key
對應(yīng)的值秕豫。PropertyPlaceholderConfigurer
檢查bean定義中大多數(shù)的placeholders
占位符,placeholder
的前綴和后綴都是自定義的。
使用Spring2.5引入的上下文命名空間观蓄,就可以用一個專用配置元素配置屬性placeholders
占位符混移。可以指定多個locations
蜘腌,多個locations
使用,
逗號分割沫屡。
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
PropertyPlaceholderConfigurer
不僅僅檢索指定的Properties
文件。默認(rèn)情況撮珠,若是在指定的Properties
配置文件中找不到指定的屬性property
,也會檢查Java 的系統(tǒng)屬性System properties
沮脖。通過設(shè)置systemPropertiesMode
屬性的值金矛,定義默認(rèn)查找行為,該屬性值有幾個取值:
- never:不檢查系統(tǒng)屬性
- fallback:如果未在指定文件中解析出屬性值勺届,則檢查系統(tǒng)屬性驶俊。此項(xiàng)為默認(rèn)行為。
- override:先檢查系統(tǒng)屬性免姿。系統(tǒng)屬性會覆蓋其他配置文件中的屬性饼酿。
PropertyPlaceholderConfigurer
更多詳情參看javadocs
TIP
可以使用PropertyPlaceholderConfigurer
替換類名,有時胚膊,某些類在運(yùn)行時才能確定故俐,那么這將非常有用。
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/foo/strategy.properties</value>
</property>
<property name="properties">
<value>custom.strategy.class=com.foo.DefaultStrategy</value>
</property>
</bean>
<bean id="serviceStrategy" class="${custom.strategy.class}"/>
若類在運(yùn)行時期間不能解析為合法類紊婉,
ApplicationContext
創(chuàng)建非延遲初始化bean的preInstantiateSingletons()
期間拋錯誤药版,
<h5 id='beans-factory-overrideconfigurer'>Example: the PropertyOverrideConfigurer</h5>
PropertyOverrideConfigurer
,是另一個ben工廠的post-processor
,類似于PropertyPlaceholderConfigurer
喻犁,但是有不同之處槽片,bean源定義可以設(shè)置默認(rèn)值或者根本不設(shè)置值。若一個overriding Properties
文件不包含某個bean屬性,就使用默認(rèn)的上下文定義肢础。
注意bean定義并不知道它會被重寫还栓,所以使用了重寫配置在XML配置中并不直觀。如果有多個PropertyOverrideConfigurer
實(shí)例為相同的bean屬性配置了不同的值传轰,最后一個實(shí)例配置生效剩盒。
Properties文件配置格式如下
beanName.property=value
舉例:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
上述文件中的配置,將會賦值給在容器中的定義的bean的相應(yīng)屬性 路召,bean的名字是datasource
,有driver
屬性和url
屬性
Compound property names are also supported, as long as every component of the path except the final property being overridden is already non-null (presumably initialized by the constructors). In this example…
同樣支持復(fù)合屬性勃刨,屬性路徑可以要多長有多長,但是屬性不能為null(),看樣例:
foo.fred.bob.sammy=123
bean foo
有屬性fred
,fred
有屬性bob
股淡,bob
有屬性sammy
,sammy
賦值為123
Note
指定重寫值都是字面值廷区;不會解析為bean引用唯灵。就算是指定的值,在XML的bean定義中bean的名字隙轻,也不會解析為該引用埠帕,而是解析為字面值。
使用Spring 2.5中引入的上下文命名空間,可以為配置屬性指定專用配置元素
<context:property-override location="classpath:override.properties"/>
<h4 id='beans-factory-extension-factorybean'>使用FactoryBean自定義實(shí)例化邏輯</h4>
對象實(shí)現(xiàn)org.springframework.beans.factory.FactoryBean
接口,則成為它本身的工廠玖绿。
FactoryBean
接口是Spring IoC容器實(shí)例化邏輯的擴(kuò)展點(diǎn)敛瓷。假如初始化代碼非常復(fù)雜,此時使用java編碼比使用XML配置更容易表達(dá)斑匪。這種場景中呐籽,就可以自定義FactoryBean
,在類中撰寫復(fù)雜的初始化程序,并將其作為插件加入到容器中。
FactoryBean
接口有3個方法:
- Object getObject():返回本工廠創(chuàng)建的對象實(shí)例狡蝶。此實(shí)例也許是共享的庶橱,依賴于該工廠返回的是單例或者是原型。
- boolean isSingleton():如果
FactoryBean
返回的是單例,該方法返回值為true
,否則為false
- Class getObjectType():返回對象類型贪惹。對象類型是
getObject()
方法返回的對象的類型苏章,如果不知道的類型則返回null。
FactoryBean
概念和接口在Spring框架中大量使用奏瞬。Spring內(nèi)置的有超過50個實(shí)現(xiàn)枫绅。
當(dāng)使用ApplicationContext
的getBean()
方法獲取FactoryBean
實(shí)例本身而不是它所產(chǎn)生的bean,則要使用&
符號+id硼端。比如并淋,現(xiàn)有FactoryBean
,它有id显蝌,在容器上調(diào)用getBean("myBean")
將返回FactoryBean
所產(chǎn)生的bean预伺,調(diào)用getBean("&myBean")
將返回FactoryBean
它本身的實(shí)例。
<h3 id='beans-annotation-config'>基于注解的把配置元數(shù)據(jù)</h3>
注解比XML好么?
注解比XML好么曼尊,簡單的說得看情況酬诀。詳細(xì)的說,各有優(yōu)缺點(diǎn)骆撇。因?yàn)槎x的方式瞒御,注解在聲明處提供了大量的
上下文信息,所以注解配置要更簡潔神郊。然而,XML擅長在不接觸源碼或者無需反編譯的情況下組裝組件肴裙。
雖然有這樣的爭議:注解類不再是`POJO`,并且配置更加分散難以控制涌乳,
但是還是有人更喜歡在源碼上使用注解配置蜻懦。
無論選擇哪一樣,Spring都能很好的支持夕晓,甚至混合也行宛乃。值得指出的是,
使用`[JavaConfig](#beans-java)`選項(xiàng)蒸辆,Spring能在不接觸目標(biāo)組件源碼的情況下
無侵入的使用注解征炼,這可以通過IDE完成 [Spring Tool Suite](https://spring.io/tools/sts)
對于XML配置,還有另外一個選擇躬贡,基于注解的配置谆奥,它是依賴于字節(jié)碼元數(shù)據(jù),替代XML組裝組件拂玻。碼農(nóng)碼畜可以使用注解替代XML描述bean的組裝酸些,開發(fā)者將配置撰寫到組件類上宰译,使用注解標(biāo)注相關(guān)的類、方法擂仍、域上囤屹。就像前面提到的 in the section called “Example: The RequiredAnnotationBeanPostProcessor”,使用BeanPostProcessor
聯(lián)結(jié)注解是常見的擴(kuò)展Spring IoC容器的手段逢渔。舉個栗子肋坚,Spring2.0引入的通過@Required
注解強(qiáng)制檢查必須屬性值。Spring 2.5采用了類似的手法使用注解處理依賴注入肃廓。本質(zhì)上智厌,@Autowired
注解提供了相同的能力,在這一章有詳解Section 5.4.5, “Autowiring collaborators”,但是@Autowired
提供了更細(xì)粒度的控制和更強(qiáng)的能力盲赊。Spirng 2.5也增加了對JSR-250注解的支持铣鹏,比如@PostConstruct
,@PreDestory
。Srping3.0增加支持了JSR-330(JAVA依賴注入)注解,這些注解在javax.inject
包內(nèi)哀蘑,例如@Inject
和@Named
诚卸。詳情參看那些注解的相關(guān)章節(jié)。
注意
注解注入在XML注入之前執(zhí)行绘迁,因此同時使用這兩種方式注入時合溺,XML配置會覆蓋注解配置。
同樣的Spring風(fēng)格缀台,就像特別的bean定義那樣注冊他們棠赛,但是也能像下面這樣隱式注冊(注意包含context namespace上下文命名空間)
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
(隱式注冊的post-processors
包括AutowiredAnnotationBeanPostProcessor
,CommonAnnotationBeanPostProcessor
,PersistenceAnnotationBeanPostProcessor
,還有前面提到的RequiredAnnotationBeanPostProcessor
)
注意
<context:annotation-config/>
僅會檢索它所在的應(yīng)用context上下文中bean上的注解。也就是膛腐,如果在WebApplicationContext
中為DispatcherServlet
設(shè)置<context:annotation-config/>
睛约,它僅會檢查controllers
中@Autowired
的bean,并不會檢查service
。詳情參看Section 17.2, “The DispatcherServlet”
<h4 id='beans-required-annotation'>@Required</h4>
@Required
注解應(yīng)用于bean的setter方法哲身,像這樣:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
這個注解意思是受到影響的bean屬性在配置時必須賦值,在bean定義中明確指定其屬性值或者通過自動注入辩涝。若該屬性未指定值,容器會拋異常勘天。這導(dǎo)致及時明確的失敗膀值,避免NullPointerExceptions
或者晚一些時候才發(fā)現(xiàn)。仍然推薦误辑,你在編碼過程中使用斷言,舉個栗子歌逢,在init
方法巾钉,做了這些強(qiáng)制的必須引用的檢查,但是屬性值甚至不再容器范圍內(nèi)秘案。
<h4 id='beans-autowired-annotation'>@Autowired</h4>
如你所料,@Autowired
注解也是應(yīng)用在"傳統(tǒng)的"setter方法上:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
注意
在下面的樣例中砰苍,使用JSR 330的@Inject
注解可以替代@autowired
注解潦匈。詳情參看這里
也可以將注解用于帶一個或多個參數(shù)的其他方法上,看樣例:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
@Autowired
也可以應(yīng)用于構(gòu)造函數(shù)上或者屬性上:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}