開發與維運

Spring 5 中文解析核心篇-IoC容器之ApplicationContext與BeanFactory

1.15 ApplicationContext的其它功能

像在這個章節討論的,org.springframework.beans.factory包提供基本的管理和操作bean的功能,包含編程式方式。org.springframework.context包添加ApplicationContext接口,它拓展BeanFactory接口,此外還擴展了其他接口以提供更多面嚮應用程序框架的樣式的附加功能。許多人使用ApplicationContext以完全聲明的方式,甚至沒有以編程方式創建它,但是取而代之的是依靠諸如ContextLoader之類的支持類來自動實例化ApplicationContext,這是Java EE Web應用程序正常啟動過程的一部分。

為了以更加面向框架的方式增強BeanFactory的功能,上下文包還提供以下功能:

  • 通過MessageSource接口獲取,在i18n獲取消息
  • 通過ResourceLoader接口,獲取資源,例如URL和文件
  • 通過使用ApplicationEventPublisher接口,將事件發佈到實現ApplicationListener接口的bean
  • 加載多個(分層)上下文,通過HierarchicalBeanFactory接口將每個上下文集中在一個特定層上,例如應用程序的Web層
1.15.1 使用MessageSource國際化

ApplicationContext接口繼承一個叫MessageSource接口並且提供國際化(i18n)功能。Spring也提供HierarchicalMessageSource接口,它能分層地解析消息。這些接口一起提供了Spring影響消息解析的基礎。這個方法定義在這些接口上:

  • String getMessage(String code, Object[] args, String default, Locale loc):這個基礎方法被使用從MessageSource中獲取消息。當指定的位置沒有找到消息,默認消息被使用。使用標準庫提供的MessageFormat功能,傳入的所有參數都將成為替換值。
  • String getMessage(String code, Object[] args, Locale loc):實質上類似前面的方法一樣,但是有一個不同的地方:沒有默認消息被指定。如果這個消息不能不找到,一個NoSuchMessageException被拋出。
  • String getMessage(MessageSourceResolvable resolvable, Locale locale):在前面方法中所有使用的屬性被一個類名為MessageSourceResolvable包裝,你可以使用這個方法。

ApplicationContext被加載時,它會自動地在上下文中搜索bean定義為MessageSource的類。這個bean必須有個messageSource名字。如果bean沒有找到,所有調用前面的方法被代理到消息源。如果沒有找到消息源,ApplicationContext嘗試去父容器查找相同名稱的bean。如果找到,則使用它作為MessageSource。如果ApplicationContext沒有找到任何消息源,一個空的DelegatingMessageSource被實例化去接受前面定義的方法調用。

Spring提供兩個MessageSource實現,ResourceBundleMessageSourceStaticMessageSource。兩者都實現HierarchicalMessageSource以便進行嵌套消息傳遞。StaticMessageSource很少被使用,但是提供編程式的方式去添加消息源。下面例子展示:

<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>

這個例子假設在你的類路徑定義有三個資源包分別是formatexceptionswindows。任何解析消息的請求都以jdk標準的方式處理,即通過ResourceBundle對象解析消息。為了這個例子的目的,假設上面兩個資源包文件內容分佈如下:

# in format.properties
    message=Alligators rock!
 # in exceptions.properties
    argument.required=The {0} argument is required.

下面例子展示一個程序去執行MessageSource功能。記住,所有的ApplicationContext實現也是MessageSources實現並且能夠轉換為MessageSource接口。

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

上面的程序輸出結果是:

Alligators rock!

總而言之,MessageSource被定義在叫做beans.xml的文件中,它存在於你的類路徑root下。messageSource bean定義通過它的basenames屬性引用一些資源包。列表中傳遞給basenames屬性的三個文件在類路徑的根目錄下以文件形式存在並且分別稱為format.propertiesexceptions.propertieswindows.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.something.Example">
        <property name="messages" ref="messageSource"/>
    </bean>

</beans>
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", Locale.ENGLISH);
        System.out.println(message);
    }
}

調用execute() 方法輸出結果:

The userDao argument is required.

關於國際化(i18n), Spring的各種MessageSource實現遵循與標準JDK ResourceBundle相同的語言環境解析和後備規則。簡而言之,並繼續前面定義的示例messageSource,如果要針對英國(en-GB)語言環境解析消息,則可以分別創建名為format_en_GB.propertiesexceptions_en_GB.propertieswindows_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中定義的bean,當MessageSource bean被創建且被配置時,實現MessageSourceAware接口將被注入應用上下文的MessageSource

作為ResourceBundleMessageSource的替代方法,Spring提供一個ReloadableResourceBundleMessageSource類。這個變體支持相同包文件格式,但是比基於標準JDK的ResourceBundleMessageSource實現更靈活。特別是,它允許從任何Spring資源為主讀取文件並且支持包屬性文件的熱加載(同時在它們之間有效地緩存它們)。查看ReloadableResourceBundleMessageSourcejavadoc詳細信息。

代碼示例:com.liyong.ioccontainer.starter.MessageSourceIocContainer

1.15.2 標準和自定義事件

ApplicationContext中事件處理是通過ApplicationEventApplicationListener接口提供的。如果bean實現ApplicationListener接口並且部署到上下文中,則每次將ApplicationEvent發佈到ApplicationContext時,都會通知該bean。實質上,這是一個標準的觀察者模式。

從Spring4.2開始,事件基礎設施已經被顯著地改善並且提供基於註解的模式以及去發佈任意事件的能力(也就是說,對象沒有必須要從ApplicationEvent拓展)。當發佈一個對象時,我們包裝為事件。

下面表格描述Spring提供的標準事件:

Event Explanation
ContextRefreshedEvent ApplicationContext被初始化或刷新(例如,通過在ConfigurableApplicationContext接口上使用refresh()方法)時發送。這裡的“initialized”意思是所有的bean被加載、post-processor(後置處理器)bean被檢查和激活、單例bean前置實例化和ApplicationContext對象已經準備好使用。只要上下文沒有被關閉,刷新能夠在多個時間觸發,前提是所選的ApplicationContext實際上支持這種“”刷新。例如,XmlWebApplicationContext支持熱刷新,但是GenericApplicationContext不支持。
ContextStartedEvent ApplicationContext通過在ConfigurableApplicationContext接口上使用start()方法啟動時發送。這裡的“started”意思是所有Lifecyclebean接收一個真實的開始信號。通常地,這個信號在顯示停止之後重啟bean使用,但也可以用於啟動尚未配置自動啟動的組件(例如,尚未在初始化時啟動的組件)
ContextStoppedEvent ApplicationContext通過在ConfigurableApplicationContext上接口上使用stop()方法停止時發佈。這裡的“stopped”意思是所有Lifecycle 的bean接受一個顯示停止信號。停止上下文可以通過調用start()重啟。
ContextClosedEvent ApplicationContext通過在ConfigurableApplicationContext接口上使用close()關閉或者通過JVM關閉鉤子時發佈。這裡的"closed"意思是所有單例bean將被銷燬。一旦上下文被關閉,它已達到使用壽命,無法刷新或重新啟動。
RequestHandledEvent 一個特定於Web的事件,告訴所有Bean HTTP請求已得到服務。這個時間在請求完成之後被髮布。這個事件僅僅能應用到使用Spring的DispatcherServlet 的web應用中。
ServletRequestHandledEvent RequestHandledEvent的子類,用於添加特定於Servlet的上下文信息。

你也可以創建和發佈你自己的自定義事件。下面例子展示了一個簡單類,它拓展了Spring的ApplicationEvent基礎類:

public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

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

    // accessor and other methods...
}

去發佈自定義ApplicationEvent,在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 content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

在配置時,Spring容器檢測EmailService實現ApplicationEventPublisherAware並且自動地調用setApplicationEventPublisher()方法。事實上,傳入的參數是Spring容器本身。你正在通過其ApplicationEventPublisher接口與應用程序上下文進行交互。

去接受自定義ApplicationEvent,你可以創建一個類實現ApplicationListener並且註冊它作為Spirng的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()方法阻塞直到所有監聽器完成事件處理。這種同步和單線程方法的一個優點是,當監聽器接收到事件時,如果事務上下文可用,它將在發佈者的事務上下文中進行操作。

如果有必要採用其他發佈事件的策略,查看javadoc對應Spring的ApplicationEventMulticaster接口和SimpleApplicationEventMulticaster實現配置。

下面例子顯示bean定義使用去註冊和配置每個類;

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>[email protected]</value>
            <value>[email protected]</value>
            <value>[email protected]</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="[email protected]"/>
</bean>

放到一起,當emailService bean的sendEmail()方法被調用時,如果這裡任何郵件信息需要被例入黑名單,一個類型為BlackListEvent自定義事件被髮布。blackListNotifier bean作為ApplicationListener和接受BlackListEvent被註冊,在這一點上,它可以通知有關各方。

Spring的事件機制被設計為在同一個應用上下文中Spring bean之間的簡單通信/交流。然而,對於更復雜的企業集成需求,單獨維護的Spring integration項目提供了對構建輕量級、面向模式、事件驅動的體系結構的完整支持,這些體系結構構建於著名的Spring編程模型之上。

基於註解事件監聽器

從Spring4.2後,你可以在任何通過使用@EventListener註解的bean,公共方法註冊一個事件監聽器。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() {
    // ...
}

也可以通過使用定義SpEL表達式的註釋的condition屬性來添加其他運行時過濾器,該註釋應匹配以針對特定事件實際調用該方法。

以下示例顯示了僅當事件的content屬性等於my-event時,才可以重寫我們的通知程序以進行調用:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}

每個SpEL表達式都會根據專用上下文進行評估。下表列出了上下文可用的項,以便你可以將它們用於條件事件處理。

Name Location Description Example
Event root object 實際的 ApplicationEvent. #root.eventevent
Arguments array root object 這些參數(對象數組)被使用去調用方法 #root.argsargs; args[0] 獲取第一個元素.
Argument name evaluation context 任何方法參數名。如,因為一些原因,這些名稱無效(例如,因為在編譯字節碼中沒有debug信息),使用#a <#arg>語法也可以使用單個參數,其中<#arg>代表參數索引(從0開始) #blEvent#a0(你可以使用 #p0 或#p<#arg>`參數符號作為別名)

請注意,即使你的方法簽名實際上引用了已發佈的任意對象,#root.event也允許你可以訪問底層事件。

如果你需要發佈一個事件作為處理其它事件結果,你可以改變方法簽名去返回事件,類似下面例子:

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

這個特性對於異步監聽是不支持的。

這個新方式通過上面的方法為每個BlackListEvent處理髮佈一個新ListUpdateEvent。如果你需要去發佈一些事件,你可以返回一個事件Collection

異步事件監聽器

如果你想一個特定監聽器去處理異步事件,你可以重用常規的@Async支持。下面例子展示怎樣使用:

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

使用異步事件時,請注意以下限制:

  • 如果一個異步事件監聽器拋出Exception,它不會被傳遞到調用者。查看AsyncUncaughtExceptionHandler更多詳情。
  • 異步事件監聽器方法不能通過返回一個值發佈一個後續事件。如果你需要發佈處理結果的其它事件,注入ApplicationEventPublisher去手動發佈事件。

監聽器順序

如果你需要一個監聽器調用在另外一個監聽器之前,你可以添加@Order註解到方法聲明上,類似下面例子:

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

泛型事件

你可以使用泛型去進一步定義你的事件結構。考慮使用EntityCreatedEvent<T>,其中T是已創建的實際實體的類型。例如,你可以創建下面的監聽器定義,為Person去接受EntityCreatedEvent

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

由於類型擦除,只有在觸發的事件解析事件監聽器過濾器所基於的通用參數(即,類似於類PersonCreatedEvent擴展EntityCreatedEvent<Person>{})的情況下才可以工作。

在某些情況下,如果所有事件都遵循相同的結構,這可能會變得很乏味(就像前面示例中的事件一樣)。在這種情況下,你可以實現ResolvableTypeProvider來指導框架,使其超出運行時環境提供的範圍(備註:通過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,而且適用於你作為事件發送的任何任意對象。

1.15.3 便捷地訪問低級別資源

為了更好的使用和理解應用上下文,你應該熟悉Spring的Resource抽象,在Resources中描述。

應用上下文是ResourceLoader,它可以被使用加載Resource對象。Resource本質上是JDK java.net.URL類的功能更豐富的版本。事實上,Resource實現包裝一個java.net.URL實例,Resource可以透明的方式從幾乎任何位置獲取低級資源,包含從類路徑、文件系統路徑、任何描述一個標準URL、以及其他的變體。如果資源位置字符串是沒有任何特殊前綴的簡單路徑,那麼這些資源的來源是特定的,並且適合於實際的應用程序上下文類型。

你可以配置部署到應用程序上下文中的Bean,以實現特殊的回調接口ResourceLoaderAware,以便在初始化時自動回調,並將應用程序上下文本身作為ResourceLoader傳入。你也可以暴露Resource類型屬性,被使用獲取靜態資源。它們像其他屬性一樣注入其中。當bean被部署時,你可以指定這些Resource屬性作為簡單String路徑並且依靠這些文本字符串自動轉換真實Resource對象。

提供給ApplicationContext構造函數的位置路徑實際上是資源字符串,並且以簡單的形式根據特定的上下文實現進行適當的處理。例如,ClassPathXmlApplicationContext將簡單的位置路徑視為類路徑位置。你也可以使用帶有特殊前綴的位置路徑(資源字符串)來強制從類路徑或URL中加載定義,而不管實際的上下文類型如何。

1.15.4 Web應用的ApplicationContext實例化

你可以通過聲明式地創建ApplicationContext實例,例如: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>

監聽器檢查contextConfigLocation參數。如果參數不存在,監聽器使用/WEB-INF/applicationContext.xml作為默認值。當參數存在時,監聽器通過使用預先定義的分隔符和使用值作為應用上下文搜索的路徑的分隔字符串。Ant格式路徑模式也支持很好。示例包括/WEB-INF/*Context.xml(適用於所有名稱以Context.xml結尾且位於WEB-INF目錄中的文件)和/WEB-INF/**/*Context.xml(適用於所有此類文件)文件在WEB-INF的任何子目錄中)。

1.15.5 部署SpringApplicationContext作為Java EE rar文件

可以將Spring ApplicationContext部署為RAR文件,並將上下文及其所有必需的bean類和庫JAR封裝在Java EE RAR部署單元中。這等效於引導獨立的ApplicationContext(僅託管在Java EE環境中)能夠訪問Java EE服務器功能。RAR部署是部署無頭WAR文件的方案的一種更自然的選擇,實際上,這種WAR文件沒有任何HTTP入口點,僅用於在Java EE環境中引導Spring ApplicationContext。RAR部署非常適合於不需要HTTP入口點,而只由消息端點和調度任務組成的應用程序上下文。在上下文中的這些bean能使用應用服務資源,例如,JTA事物管理、JNDI綁定JDBC DataSource實例、JMS ConnectionFactory實例和註冊平臺的JMX服務-通過所有Spring的標準事物管理、JNDI、JMX支持的設施。應用組件也可以通過Spring的TaskExecutor抽象與應用server的JCA WorkManager相互交互 。

有關RAR部署中涉及的配置詳細信息,請參見SpringContextResourceAdapter類的javadoc。

對於將Spring ApplicationContext作為Java EE RAR文件的簡單部署:

  1. 打包應用所有類到RAR文件中(這是具有不同文件擴展名的標準JAR文件)。將所有必需的庫JAR添加到RAR歸檔文件的根目錄中。添加一個META-INF/ra.xml部署描述符(如SpringContextResourceAdapter的javadoc中所示和相應的Spring XML bean定義文件(通常為META-INF/applicationContext.xml)。
  2. 將生成的RAR文件拖放到應用程序服務器的部署目錄中

此類RAR部署單位通常是獨立的。它們不會將組件暴露給外界,甚至不會暴露給同一應用程序的其他模塊。與基於RAR的ApplicationContext的交互通常是通過與其他模塊共享的JMS目標進行的。例如,基於rar的ApplicationContext還可以調度一些作業或對文件系統中的新文件(或類似的東西)作出反應。如果它需要允許來自外部的同步訪問,它可以(例如)導出RMI端點,這些端點可以由同一臺機器上的其他應用程序模塊使用。

1.16 BeanFactory

BeanFactory API為Spring IoC工廠提供底層基礎。它的特定契約主要用於與Spring的其他部分和相關第三方框架的集成,它的DefaultListableBeanFactory實現是更高級別的GenericApplicationContext容器中的一個關鍵代理。

BeanFactory和相關的接口(例如:BeanFactoryAwareInitializingBeanDisposableBean)是為其他框架組件非常重要的集成點。通過不需要任何註解,甚至不需要反射,它們可以在容器及其組件之間進行非常有效的交互。應用級別bean可能使用相同回調接口,但通常更喜歡通過註釋或通過編程配置進行聲明式依賴注入。

請注意,核心BeanFactory API級別及其DefaultListableBeanFactory實現不對配置格式或要使用的任何組件註釋進行假設。所有這些風格都通過擴展(比如XmlBeanDefinitionReaderAutowiredAnnotationBeanPostProcessor)實現,並在共享的BeanDefinition對象上進行操作,作為核心元數據表示。這是使Spring的容器如此靈活和可擴展的本質所在。

1.16.1 BeanFactoryApplicationContext

本節說明BeanFactoryApplicationContext容器級別之間的區別以及對引導的影響。

除非有充分的理由,否則應使用ApplicationContext,除非將GenericApplicationContext和其子類AnnotationConfigApplicationContext作為自定義引導的常見實現,否則應使用ApplicationContext。這些是用於所有常見目的的Spring核心容器的主要入口點:加載配置文件、觸發類路徑掃描、以編程方式註冊Bean定義和帶註解的類,以及(從5.0版本開始)註冊功能性Bean定義。

因為ApplicationContext包含BeanFactory的所有功能,所以通常建議在普通BeanFactory中使用,除非需要完全控制Bean處理的方案。在ApplicationContext(例如GenericApplicationContext實現)中,按照約定(即,按bean名稱或按bean類型(尤其是後處理器))檢測到幾種bean,而普通的DefaultListableBeanFactory則與任何特殊bean無關。

對於許多擴展的容器功能,例如註解處理和AOP代理,BeanPostProcessor擴展點是必不可少的。如果僅使用普通的DefaultListableBeanFactory,則默認情況下不會檢測到此類後處理器並將其激活。這種情況可能會造成混淆,因為你的bean配置實際上並沒有錯。而是在這種情況下,需要通過其他設置完全引導容器。

下表列出了BeanFactoryApplicationContext接口和實現提供的功能。

Feature BeanFactory ApplicationContext
Bean 實例化/連接 Yes Yes
集成生命週期管理 No Yes
BeanPostProcessor自動註冊 No Yes
BeanFactoryPostProcessor自動註冊 No Yes
便捷的MessageSource獲取(國際化) No Yes
內建ApplicationEvent發佈機制 No Yes

要向DefaultListableBeanFactory顯式註冊Bean後處理器,需要以編程方式調用addBeanPostProcessor,如以下示例所示:

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions

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

// now start using the factory

要將BeanFactoryPostProcessor應用於普通的DefaultListableBeanFactory,你需要調用其postProcessBeanFactory方法,如以下示例所示:

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

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

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

在這兩種情況下,顯式的註冊步驟都不方便,這就是為什麼在Spring支持的應用程序中,各種ApplicationContext變量比普通的DefaultListableBeanFactory更為可取的原因,尤其是在典型企業設置中依賴BeanFactoryPostProcessorBeanPostProcessor實例來擴展容器功能時。

AnnotationConfigApplicationContext已註冊了所有常見的註解後處理器,並且可以通過配置註解(例如@EnableTransactionManagement)在幕後引入其他處理器。在Spring基於註解的配置模型的抽象級別上,bean後處理器的概念僅是內部容器詳細信息。

作者

個人從事金融行業,就職過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就職於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公眾號和博客站點對知識體系進行分享。

博客地址: http://youngitman.tech

CSDN: https://blog.csdn.net/liyong1028826685

微信公眾號:

技術交流群:

Leave a Reply

Your email address will not be published. Required fields are marked *