5

注意LifecycleProcessor接口繼承了Lifcycle接口狮鸭。同時善玫,增加了2個方法洒敏,用于處理容器的refreshedclosed事件。

startupshutdown方法調(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對象之后停止),使用 正值則恰恰相反媒咳。

如你所見粹排,SmartLifecyclestop()方法有一個回調(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接口也定義了容器的refreshingclosing事件拖陆。后者會驅(qū)動shutdown處理弛槐,就像是明確的調(diào)用了stop()方法,但是它是發(fā)生在容器關(guān)閉期間。refresh回調(diào)開啟了SmartLifecyclebean的另一個功能 依啰。當(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)的constructorbyType自動裝配模式(詳情參看 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)之前猾漫,比如InitializingBeanafterPropertiesSet或者 自定義的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

除上面討論過的ApplicationContextAwareBeanNameAware点晴,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屬性,則必須指定abstracttrue,這是非常重要的,否則容器會預(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è)置BeanPostProcessorsorder屬性來控制其執(zhí)行次序侈净。讓BeanPostProcessor實(shí)現(xiàn)Ordered接口尊勿,就能設(shè)置次屬性。如果使用自定義BeanPostProcessor畜侦,也得考慮實(shí)現(xiàn)Ordered接口元扔。更多的細(xì)節(jié),參閱BeanPostProcessorOrdered接口的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,在容器初始化方法(比如InitializingBeanafterProperieSet()方法和其他所有的聲明的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,比如PropertyOverrideConfigurerPropertyPlaceholderConfigurer坦辟。也可以使用自定義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有屬性sammysammy賦值為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)使用ApplicationContextgetBean()方法獲取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;
    }

    // ...

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赚导,一起剝皮案震驚了整個濱河市茬缩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吼旧,老刑警劉巖凰锡,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異圈暗,居然都是意外死亡掂为,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門员串,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勇哗,“玉大人,你說我怎么就攤上這事寸齐∮担” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵渺鹦,是天一觀的道長扰法。 經(jīng)常有香客問我,道長海铆,這世上最難降的妖魔是什么迹恐? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮卧斟,結(jié)果婚禮上殴边,老公的妹妹穿的比我還像新娘。我一直安慰自己珍语,他們只是感情好锤岸,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著板乙,像睡著了一般是偷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上募逞,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天蛋铆,我揣著相機(jī)與錄音,去河邊找鬼放接。 笑死刺啦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的纠脾。 我是一名探鬼主播玛瘸,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蜕青,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了糊渊?” 一聲冷哼從身側(cè)響起右核,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渺绒,沒想到半個月后贺喝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芒篷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年搜变,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片针炉。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡挠他,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篡帕,到底是詐尸還是另有隱情殖侵,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布镰烧,位于F島的核電站拢军,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏怔鳖。R本人自食惡果不足惜茉唉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望结执。 院中可真熱鬧度陆,春花似錦、人聲如沸献幔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜡感。三九已至蹬蚁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郑兴,已是汗流浹背犀斋。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谦去。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像巡莹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子臊岸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容