spring_ioc的其餘功能 (7.14節以後)

###7.14 Registering a LoadTimeWeaver 當類被加載到JVM中時,LoadTimeWeaver將動態轉化類. 要使用運行時織入器,要在你的@Configuration類裏添加@EnableLoadTimeWeaving註解.html

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {

}

另外一種方式是在xml配置裏使用context:load-time-weaver元素:java

<beans>
    <context:load-time-weaver/>
</beans>

當ApplicationContext一旦配置,任何在ApplicationContext內的實現了LoadTImeWeaverAware接口的Bean,會獲得一個加載期織入器實例的引用.在聚合Spring jpa support 時很是有用, 由於對JPA的類轉譯而言,運行時織入是必須的.查找LocalContainerEntityManagerFactoryBean的文檔來了解更多.對於更多 AspectJ 加載期織入的,參看 11.8.4 "load-time weaving with AspectJ in the Spring Framework".web

##7.15 Additional Capabilities of the ApplicationContext(應用上下文的其餘功能) 如同咱們在章節介紹中討論的,org.springframework.beans.factory包提供了管理和操做beans的基本功能,包括用編程方式.這個org.springframework.context包添加了ApplicationContext,它擴展了BeanFactory接口,並以更多的應用面相框架風格來提供更多的接口以實現更多的功能.許多人以徹底聲明的方式使用ApplicationContext,甚至不用程序建立它,而是依賴相關的額外支持類,如ContextLoader,用來自動實例化ApplicationContext,以做爲java EE web應用正常啓動流程的一部分.spring

要保證BeanFactory的功能更面向框架,那麼上下文包也須要提供如下功能:編程

  • 以 i18n-style(國際化) 的風格訪問消息,經過MessageSource接口.
  • 訪問資源,例如以URLS或文件,經過ResourceLoader接口.
  • 經過使用 ApplicationEventPublisher接口,事件可發佈到以實現了ApplicationListener接口的已命名的bean中
  • 多層次上下文加載時,經過HierarchicalBeanFactory 接口,容許每一個人只關注一個特定的層次,例如一個應用的web層;

###7.15.1 Internationalization using MessageSource(經過消息資源實現國際化) ApplicationContext接口擴展了一個名爲MessageSource的接口,並提供了國際化功能.spring還提供了HierarchicalMessagesource接口,它能夠層次化處理消息.這些接口在一塊兒構成了spring相應消息處理的基礎.這些接口的方法包括:windows

  • String getMessage(String code,Object[] args,String default,Local loc):這是從MessageSource抓取消息的基本方法.當特定的位置沒有發現消息時,默認的消息會使用.任何經過的參數變成特定值,可使用標準類庫裏的messageFormat功能.設計模式

  • String getMessage(String code,Object[] args,Locale loc):和上一個方法基本上是相同的,但有一點不一樣:因沒有指定默認值,全部當沒有消息被發現,一個NoSuchMessageException會被拋出.數組

  • String getMessage(MessageSourceResolvable resolvable,Locale locale):上面方法全部的屬性都會被包裹進一個名爲MessageSourceResolvable的類裏,你也可使用這個方法替代.安全

當一個ApplicationContext被加載時,它會自動搜索每個在上下文中定義的MessageSource的bean.這個Bean必須命名爲messageSource.若是該bean被發現,上面全部對該消息資源的方法的調用都是代理形式.若是沒有發現消息資源,那麼ApplicationContext將嘗試尋找其上級的包含了相同名字的bean.若是這樣,那麼它會使用名爲MessageSource的bean.若是ApplicationContext仍是沒法發現消息資源,一個空的DelegatingMessageSource將會實例化以用來接受上面方法的調用.服務器

Spring提供了兩種MessageSource的實現,ResourceBundleMessageSource和StaticMessageSource.兩者都實現了HierarchicalMessageSource以處理嵌套消息.StaticMessageSource不多使用,但它提供了編程的方式向資源中添加消息.而ResourceBundleMessageSource以下使用:

<beans>
    <bean id="messageSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>format</value>
                <value>exceptions</value>
                <value>windows</value>
            </list>
        </property>
    </bean>
</beans>

上面的例子中代表你在類路徑裏有三種資源綁定定義,名字爲:format,exceptions ,windows.任何釋放消息的請求,會被經過ResourceBundeles以JDK標準釋放消息的方式來處理.對於這些例子的目標,假設如下兩種資源綁定文件的內容以下:

# in format.properties
message=Alligators rock!

# in exceptions.properties
argument.required=The {0} argument is required.

下面的例子展現的是一段執行MessageSOurce功能的程序.記住全部ApplicationCOntext的實現同時也是MessageSource的實現,能夠轉化爲MessageSource接口.

public static void main(String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("message", null, "Default", null);
    System.out.println(message);
}

輸出的結果以下:

Alligators rock!

全部來回顧下,在beans.xml裏定義的MessageSource,該文件存在於類路徑的根路徑下.messageSource的bean定義經過它的basenames屬性指向大量的資源定義.這三個在beannames屬性列表裏的的文件存在於你的類路徑的根節點下,分別是format.properties,exceptions.properties,windows.properties.

下面的例子讓我看下消息查找的參數,這些參數會轉化到String中,並插入到查找消息的佔位符裏.

<beans>
    <!-- this MessageSource is being used in a web application -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="exceptions"/>
    </bean>

    <!-- lets inject the above MessageSource into this POJO -->
    <bean id="example" class="com.foo.Example">
        <property name="messages" ref="messageSource"/>
    </bean>

</beans>

經過xml,設置參數依賴,將messageSource注入到example的Bean中;

public class Example {

    private MessageSource messages;

    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }

    public void execute() {
        String message = this.messages.getMessage("argument.required",
            new Object [] {"userDao"}, "Required", null);
        System.out.println(message);
    }

}

這個輸出結果是:

The userDao argument is required.

關於國際化,spring的各類MessageSource實現遵循和標準JDK ResourceBundle同樣的場景方案和回滾規則.繼續上面的messageSource的定義.若是你要在英文場景下釋放這些消息,簡單的說,你須要分別建立這三個文件,format_en_GB.properties,exceptions_en_GB.properties,windows_en_GB.properties.

通常而言,場景解決主要有應用的周邊環境來管理.在本例中,消息的英文場景會手動指定;

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the {0} argument is required, I say, required.

public static void main(final String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("argument.required",
        new Object [] {"userDao"}, "Required", Locale.UK);
    System.out.println(message);
}

上面例子的輸出結果爲:

Ebagum lad, the 'userDao' argument is required, I say, required.

你可使用MessageSourceAware接口來得到一個已經定義了的MessageSource接口的引用.定義在ApplicationContext中實現了MessageSourceAware接口的bean,當它們建立時會自動注入應用上下文的MessageSource.

做爲ResourceBundleMessageSource的替換,spring提供了一個ReloadableResourceBundleMessageSource類.這個變種支持相同的文件綁定格式,但比基於標準JDK的ResourceBundleMessageSource實現要更加靈活.特別,它容許從任何spring的資源位置(不只僅類路徑)讀取文件,並支持對綁定屬性文件的熱重載(重載期間有效的緩衝).查看ReloadableResourceBundleMessageSource文檔來肯定更多細節.

###7.15.2 Standard and Custom Events (標準和自定義事件) 在ApplicationCOntext中的事件處理是經過ApplicationEvent類和ApplicationListener接口來實現的.若是有一個實現了ApplicationListener接口的bean部署到上下文中,每當一個ApplicationEvent發佈到ApplicationContext時,這個bean會被調用.基本上,這就是標準的Observer(觀察者)設計模式;

在spring4.2後,事件組件獲得了明顯的提高,並提供了基於註解的模式已發佈任意事件,也就說一個對象沒有必要繼承ApplicationEvent類.當符合條件的對象發佈時,咱們爲你將該對象包裝到一個事件裏;

spring提供了一下標準事件. ###Built-in Events 輸入圖片說明 輸入圖片說明 ####ContextRefreshedEvent 上下文刷新事件

當ApplicationContext已初始化或已刷新時,會發布.例如,使用ConfigurableApplicationContext接口中的refresh()方法."initialized"意味着全部的bean都已被加載,後處理器的bean都被檢測和啓動,單例bean都被提早實例化,且ApplicationContext對象已作好準備使用.只要上下文沒被關閉,有特定的ApplicationContext提供的熱刷新,能夠被觸發不少次.

####ContextStartedEvent 上下文啓動事件 發佈:當ApplicationContext啓動,會使用ConfigurableApplicationContext接口裏的start()方法."Started"意味着全部生命週期的bean收到了一個明確的啓動信號.通常而言,這個信號只用來在一個明確的中止後重啓beans,可是它能夠被用來啓動一些沒被設置爲自動啓動的bean,例如一些在初始化中沒被啓動的組件. ####ContextStoppedEvent 上下文中止事件

當使用ConfigurableApplicationContext接口中的stop()方法時,上下文就會中止,事件發佈."Stopped"這裏指全部的生命週期的beans都會受到一個明確的中止信號.一箇中止的上下文能夠經過start()方法重啓.

ContextClosedEvent 上下文關閉事件

當調用ConfigurableApplicationContext接口的close()方法時,ApplicationContext就會關閉,事件就會發布."Closed"這裏指的是全部的單例bean都會被銷燬.一個關閉的上下文已到達它生命的端點,他不可能被刷新或重啓.

RequestHandledEvent 請求處理事件

一個特定的web事件,告訴全部的bean要爲一個HTTP請求提供服務.當請求拼完後這個事件就會發布.這個事件只有當一個web應用使用spring的DispatcherServlet服務時才適用.

你能夠建立和發佈你的自定義事件.這個例子展現了一個擴展了Spring ApplicationEvent基類的簡單類.

public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String test;

    public BlackListEvent(Object source, String address, String test) {
        super(source);
        this.address = address;
        this.test = test;
    }

    // accessor and other methods...

}

要發佈一個自定義事件,須要調用ApplicationEventPublisher實例上的publishEvent()方法.通常而言,能夠經過實現ApplicationEventPublisherAware來建立一個類並將它註冊爲一個spring bean.下面的例子展現如何使用這個類:

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String text) {
        if (blackList.contains(address)) {
            BlackListEvent event = new BlackListEvent(this, address, text);
            publisher.publishEvent(event);
            return;
        }
        // send email...
    }

}

在配置期間,spring容器會檢測到EmailService實現了ApplicationEventPublisherAware接口,並自動調用setApplicationEventPublisher()方法.實際上,傳入的參數將是spring容器自身;你能夠經過ApplicationEventPublisher接口來簡單的同應用上下文交互.

要接受一個自定義ApplicationEvent事件,需建立一個實現了ApplicationListener接口的類並註冊它爲bean.下面的例子展現了一個這樣的類:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }

}

能夠發現ApplicationListener一般是經過你的自定義事件的類型(BlackListEvent類)來泛型化參數的.這意味着onApplicationEvent()方法能夠保持類型安全,避免進行向下轉型.你能夠註冊很是多的監聽器,但要記住事件監聽器默認會進行同步監聽.這意味着只有當全部的監聽器處理完這個事件以後,publishEvent()方法纔會中止.這種同步和單線程處理的優勢之一是:若是一個監聽器接受到事件,當它能從發佈者那裏得到一個事務上下文,那它會在上下文裏處理事件.若是有必要使用其餘的事件發佈策略,查看spring的ApplicationEventMulticaster接口.

下面的例子展現了用來註冊和配置每一個類的bean定義.

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>known.spammer@example.org</value>
            <value>known.hacker@example.org</value>
            <value>john.doe@example.org</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="blacklist@example.org"/>
</bean>

整合全部,當emailService的bean的sendEmail()方法被調用時,若是有一個email是黑名單中,一個BlackListEvent類型的自定義事件就會被髮布.而blackListNotifier的bean被註冊爲ApplicationListener(應用監聽器)被會接受這個BlackListEvent事件,此時它就會通知適當的部分.

spring的事件機制主要用於同一應用上下文中spring bean之間的簡單交互.可是,對於更加複雜的企業集成需求,這個分佈式的spring集成項目提供了對基於spring編程模式來搭建輕量級,面向模式,事件驅動的架構的全面支持.

####Annotation--based Event Listeners 基於註解的事件監聽器 在spring4.2以後,一個事件監聽器能夠經過在一個受管理的bean裏的一個公共方法上添加EventListener註解來註冊.這個BlackListNotifier能夠以下重寫:

public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }

}

如同上面咱們看到的,這個方法時間上參考你要監聽的事件類型.只要一個實際的事件發佈,你就要攔截該泛型參數,只有符合內嵌的泛型,該方法纔會工做.

若是你要攔截幾種事件類型,又或者你不打算指定方法的參數,這個事件類型能夠在註解上指定;

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {

}

咱們能夠經過註解的condition屬性來添加額外的運行時攔截條件,該屬性定義了一個SpEL表達式,你須要匹配這個特定事件,才能調用相應的方法.

例如,咱們的通知器能夠重寫爲只有當事件的test屬性等於foo時纔會調用.

@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

每一次SpEL表達式評估都有專門的上下文.下面的表格展現了你在該上下文中能得到的選項,全部你可使用他們進行有條件的事件處理:

####Event Spel available metadata 輸入圖片說明 名稱:event 位置:root object 描述:實際的ApplicationEvent(應用事件),例子:#root.event

名稱:args 位置:root Object 描述:調用目標使用的參數(數組) 例子:#root.args[0]

名稱:argument name 位置:evaluation context 描述:方法中每一個參數的名字.若是因某些緣由這些名字不可得到,這個參數的名稱仍能夠經過#a<#arg>來獲取,這個#arg表明的是參數的下標(從0開始). 例子:#iban或#a0(也可使用#p0或使用#p<#arg>標誌做爲別名)

記住#root.event容許你訪問對應的事件,即便你的方法簽名實際上對應的是任意要發佈的對象.

若是你由於處理完某個事件後須要發佈一個新事件,只須要把方法簽名的返回修改成你須要發佈的事件的類型,像這樣:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

該功能不支持異步的監聽器;

這個新方法會爲每一個該方法處理過的blackListEvent發佈一個新的ListUpdateEvent事件.若是你須要發佈幾個事件,只須要返回這些事件的集合.

####Asynchronous Listeners 同步監聽器

若是你想要一個特別的監聽器來異步處理事件,簡單的使用常規的@Async支持:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

記住使用異步事件時注意如下幾個侷限:

  • 1.若是監聽器拋出一個異常,它不會傳播到調用者那裏.查看AsyncUncaughtExceptionHandler以應對更多細節.
  • 2.這些事件監聽器沒法發送回覆.若是你須要發送其餘事件做爲處理結果,注入ApplicationEventPublisher進行手動發送.

####Ordering Listeners 監聽器排序 若是你想要一個監聽器先於其餘調用.只須要在方法參數上添加@Order註解;

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

####Generic Events 泛型事件 你可使用泛型更深刻的定義你的事件的結構.思考一個EntityCreatedEvent<T>,其中T是被建立實體的實際類型.你能夠建立一個只用來接受Person類的EntityCreatedEvent事件監聽器;

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

因爲類型擦除,只有當事件無缺符合事件監聽器攔截的泛型參數,它才工做.(這相似如class PersonCreatedEvent extends EntityCreatedEvent<Person>{...});

在特定的環境下,若是有全部的事件都遵循相同的結構(它多是上面提到的那種事件)會變得至關乏味.這樣的話,你可用實現ResolvableTypeProvider來指導運行時環境提供的架構.

public class EntityCreatedEvent<T>
        extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(),
                ResolvableType.forInstance(getSource()));
    }
}

它不只只對ApplicationEvent事件其做用,還適用於你做爲事件發佈的任意對象.

###7.15.3 Convenient access to low-level resources (有效的訪問低級資源) 做爲應用上下文的可選使用和理解,用戶應該基本上經過Spring Resource abstraction 抽象來熟悉它們;同第8章所講的;

一個應用上下文是一個資源加載器,能夠用來加載Resource.一個資源本質上是JDK類中的java.net.URL類的更多功能的變種.實際上,java.net.Resource的實現以合適的方式來包裝URL的實例.一個Resource能夠以透明的方獲取低層次的資源,包括類路徑,文件系統位置,任何能被標準RUL描述的資源,或者其餘類型.若是這個資源位置字符串是一個不帶任何前綴的簡單路徑,這些資源來自於特定和合適的實際應用上下文類型.

你能夠配置一個bean部署到應用上下文中來實現指定的回調接口->ResourceLoaderAware,在應用上下文初始化時會做爲ResourceLoader而自動回調.你能夠暴露Resource類型的屬性,以用來訪問靜態資源;他們也能夠像其餘屬性同樣被注入.你能夠將這些Resource屬性指定爲簡單的字符串路徑,它依賴的一個特定的JavaBean是PropertyEditor已被自動註冊到上下文中,該javaBean部署以後能夠將文本字符串轉化爲時間的資源對象.

這個提供給ApplicationContext構造器的位置路徑是真實的資源字符串,並以簡單的方式妥善的被特定的上下文實現進行處理.ClassPathXmlApplicationContext能夠把一個簡單的位置路徑做爲一個類路徑位置.你也可使用帶具體前綴的位置路徑來從類路徑或URL來強制加載定義,而忽略實際的上下文類型.

###7.15.4 Convienent ApplicationContext instantiation for web applications 例如,你可使用一個ContextLoader建立一個ApplicationContext實例.固然你也可使用ApplicationContext的某個實現來動態建立ApplicationContext的一個實例.

你能夠以下使用ContextLoaderListener來註冊一個ApplicationContext:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

監聽器會檢驗上下文配置位置的參數.若是參數不存在,那麼監聽器會默認使用/WEB-INF/applicationContext.xml.當參數存在,那這個監聽器戶會經過已定義的分隔符(逗號,分號,空白符)來分割該字符串,並將這些值做爲做爲應用上下文要查詢的位置.Ant風格的匹配符一樣支持.例如/WEB-INF/*Context.xml可用於全部以Context.xml結尾的文件.若是存在於"WEB_INF"目錄,併爲/WEB-INF/**/*Context.xml,那麼使用於全部"WEB-INF"下的子目錄;

###7.15.5 Deploying a Spring ApplicationContext as a Java EE RAR file (以java EE RAR文件來部署spring應用上下文) 你能夠把spring應用上下文做爲一個RAR文件來部署,包括這個上下文和它全部必須的bean類,以及部署在java EE RAR部署單元裏的類庫JAR包.這等同在一個單獨的ApplicationContext,在一個java EE環境裏,能夠訪問java EE服務設施.RAR部署的是無需處理的WAR文件部署的另外一個更加天然的替代物,實際上,一個WAR文件無需任何HTTP接觸點就能夠啓動並只能用於在java EE環境裏啓動一個spring應用上下文.

RAR部署對於應用上下文來講很理想,不須要http觸發點,只須要一些消息端點和定時任務.上下文中的Beans也可使用應用服務器的資源,例如JTA事務管理,JNDI-bound JDBC DataSources,JMS ConnectionFactory instance,或許還有已經過平臺註冊的JMX服務器;都是經過spring標準事務管理和JNDI,JMX支持組件實現的.應用組件也能夠經過spring的TaskExecutor抽象同應用服務器的JCA工做管理器交互.

查看SpringContextResourceAdapter類的文檔來獲取RAR部署的更多細節.

簡單把一個spring應用上下文作爲一個java EE RAR文件部署:打包全部的應用類到一個RAR文件裏,這是一個有不一樣文件擴展名的標準JAR文件.在RAR結構裏的根位置裏添加全部必須的類庫JARs.添加一個"META-INF/ra.xml"部署描述器(在SpringContextResourceAdapter的文檔裏有描述)和對應的spring xml bean定義的文件(通常是META-INF/applicationContext.xml文件),並把生成的RAR文件剪貼到應用服務器的部署目錄.

這些RAR部署單元一般有很強的獨立性,它們不會向外界暴露接口,就是同一應用的其餘模塊也不行.(像電網UAP項目裏的模塊項目同樣);基於RAR的應用上下文之間的交互是經過與其餘模塊共享的JMS端點來實現的.一個基於RAR的系統也能夠有其餘功能:例如,安排些任務,對文件系統裏的新文件作些反應.若是它須要和外界進行同步交互,它容許你導出RMI 端點,固然這也適用於同一機制下的其餘應用模塊.

##7.16 The BeanFactory BeanFactory提供了spring ioc功能的基礎,但如今它只在集成第三方框架和spring的老用戶直接使用.BeanFactory和相關的接口,例如BeanFactoryAware,InitializingBean,DisposableBean等依然在spring中存在,主要是爲了照顧spring中集成的第三方框架的向後兼容性.通常第三方的組件沒法使用太新的的東西,好比@PostConstruct或@PreDestroy,所以要保持對jdk1.4的兼容或避免使用JSR-250的依賴.

這部分介紹了BeanFactory和ApplicationContext的差別的 額外背景,以及如何經過一個類的單例查找來訪問IOC容器. ###7.16.1 BeanFactory or ApplicationContext? 除非你有更好的理由,不然請使用ApplicationContext;

由於ApplicationContext包含了全部BeanFactory的功能;建議使用BeanFactory的狀況通常是:在一個資源受限的應用上運行項目,由於此時資源開銷會很是重要,即便幾KB也會致使極大不一樣.可是,對於絕大多數典型的企業應用和系統來言,你仍是須要ApplicationContext的.spring大量使用了BeanPostProcessor 的extension point(進行高效代理或其餘).假如你只使用簡單的BeanFactory,如事務管理和AOP等不少支持將不起做用,至少在你的代碼裏不會有額外的步驟.這種狀況會令你困惑,由於你的配置裏沒有其餘錯誤.

下面列表提供了BeanFactory和ApplicationContext接口和實現提供的功能;

BeanFactory : 1; ApplicationContext:2

  • Bean instantiation/wiring 1:yes 2:yes
  • Automatic BeanPostProcessor registration 1:NO, 2:yes
  • Automatic BeanFactoryPostProcessor registration 1:no 2:yes
  • Convenient MessageSource access(for i18n) 1:no 2:yes -ApplicationEvent publication 1:no 2:yes

要明確使用BeanFactory實現來註冊一個後處理器,你須要這樣編碼:

DefaultListableBeanFactory factory=new DefaultListableBeanFactory();

// populate the factory with bean definitions

// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);

// now start using the factory

要使用BeanFactory實現來註冊一個BeanFactoryPostProcessor,你須要這麼寫:

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));

// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));

// now actually do the replacement
cfg.postProcessBeanFactory(factory);

在這些步驟中,具體的註冊步驟並不重要;這也是大部分各類ApplicatinContext實現更傾向基於使用BeanFactory來實現,特別是當使用BeanFactoryPostProcessor和BeanPostProcessor時.這些機制實現了很是重要的功能,如屬性替代符替換和AOP;

###7.16.2 Glue code and the evil singleton (大泥球代碼和討厭單例) 你最好使用依賴注入(DI)的風格來寫應用代碼,這些代碼將由spring ioc提供服務,在其建立時就會獲得容器提供的依賴,且徹底不須知道容器.可是,對於一小部分膠水層的代碼,它們須要和其餘代碼鏈接在一塊兒,有時你須要單例(等同單例)風格來訪問spring ioc的容器.例如,第三方的代碼會無論是否能從spring的ioc容器裏得到這些對象而直接嘗試建立新的對象(Class.forName()風格).若是被第三方代碼構造的是一個小的存根或代理,它可使用單例風格來訪問spring ioc容器來獲得它要代理的實際對象,那麼控制反轉對於大部分代碼(容器以外生成的對象)來講仍是可使用.由於這些代碼仍不須要關心容器或關心如何訪問它,保持了和其餘代碼的解耦,並保持必要的便利.EJBs也可使用這種stub/proxy方式從spring ioc容器裏獲取 一個簡單java對象的代理.然而spring ioc容器自身最好不是必須是個單例bean,這不現實;由於每一個bean來使用本身容器的內存開銷或初始化時間是(當使用spring IOC容器裏的bean例如一個hibernate的SessionFactory)不同,全部最好是非單例的spring IOC容器.

以服務定位的風格來查找應用上下文有時是訪問公共spring-managed 組件的惟一方式,例如在一個EJB2.1環境,又或者當你想分享一個單例應用上下文做爲WAR文件裏的父級webApplicationContexts.這種狀況下你應該使用springteam blog entry裏描述的有用類ContextSingleBeanFactoryLocator定位器


Resource抽象,java.net.URL,ResourceLoader,ResourceLoaderAware;contextConfigLocation;RAR文件,JTA;BeanFactory,glue code ,evil singleton

相關文章
相關標籤/搜索