首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

AOP@Work: 結合 AspectJ 與 Spring 實現進階依賴注入

     編輯:Java 相關知識

前言:依賴注入與 AOP 的完美結合

依賴注入(Dependency Injection,DI)與面向切面程式設計(Aspect-Oriented Programming,AOP)是企業級應用開發中兩大關鍵技術。它們相輔相成,共同致力於簡化領域模型與應用分層。DI 封裝了資源發現與協調器的細節,而切面則封裝了中介軟體服務調用(如事務與安全管理)的複雜性。兩者結合不僅能產出更簡單、更易測試的物件導向應用,更能將 DI 的能力延伸至更廣泛的物件與服務,同時利用 DI 來配置切面本身。

核心角色與職責分析

在基於 DI 的解決方案中,代理(通常由 Spring 等輕量級容器扮演)肩負四大核心職責:

  1. 識別物件何時需要配置(通常因剛被建立)
  2. 確定物件的依賴項
  3. 發現滿足依賴的物件
  4. 以依賴項配置物件

Spring 容器同時負責建立與配置核心應用物件(Bean),透過查詢 XML 定義的元模型確定依賴,並從容器管理的 Bean 倉庫中取得相依物件,最後透過 Setter 注入、建構子注入或查詢方法注入完成配置。

單例切面的依賴注入配置

切面同樣受益於 DI 配置。最佳實踐是將切面實作為輕量級控制器:切面判斷何時觸發行為,實際工作委託給協作者。以遠端異常處理切面為例:

public aspect RemoteExceptionHandling {
    private RemoteExceptionHandler exceptionHandler;
    public void setExceptionHandler(RemoteExceptionHandler aHandler) {
        this.exceptionHandler = aHandler;
    }
    pointcut remoteCall() : call(* *(..) throws RemoteException+);
    after() throwing(RemoteException ex) : remoteCall() {
        if (exceptionHandler != null)
            exceptionHandler.onRemoteException(ex);
    }
}

由於 AspectJ 切面由 AspectJ 執行期建立,Spring 無法直接實例化。對於單例切面,AspectJ 提供 aspectOf() 工廠方法。Spring 配置如下:

<beans>
  <bean name="RemoteExceptionHandlingAspect"
        class="org.aspectprogrammer.dw.RemoteExceptionHandling"
        factory-method="aspectOf">
    <property name="exceptionHandler">
        <ref bean="RemoteExceptionHandler"/>
    </property>
  </bean>
  <bean name="RemoteExceptionHandler"
        class="org.aspectprogrammer.dw.DefaultRemoteExceptionHandler"/>
</beans>

透過 ApplicationContext 的預先實例化機制,確保切面在遠端異常拋出前完成配置。

非單例切面與領域物件的配置挑戰

對於 perthis、pertarget、percflow 等非單例切面,以及容器管控外建立的領域實體,Spring 無法預先實例化。需要新策略識別「物件已建立需配置」。

引入 SpringConfiguredObjectBroker 切面,實作 ApplicationContextAware,攔截標註 @SpringConfigured 之物件的初始化連接點,請求 Spring 套用屬性值:

public aspect SpringConfiguredObjectBroker implements ApplicationContextAware {
    private ConfigurableListableBeanFactory beanFactory;
    public void setApplicationContext(ApplicationContext aContext) { ... }
    pointcut springConfiguredObjectCreation(Object newInstance, SpringConfigured scAnnotation) :
        initialization((@SpringConfigured *).new(..)) && this(newInstance) && @this(scAnnotation);
    after(Object newInstance, SpringConfigured scAnn) returning : springConfiguredObjectCreation(newInstance, scAnn) {
        String beanName = getBeanName(newInstance, scAnn);
        beanFactory.applyBeanPropertyValues(newInstance, beanName);
    }
    private String getBeanName(Object obj, SpringConfigured ann) { ... }
}

使用時僅需標註類別:@SpringConfigured("AccountBean") public class Account { ... }。此技術適用於任何容器管控外建立的物件,包括各類生命週期的切面。

基於介面的注入

透過合約介面宣告需求,實作 AccountOperationValidationClient 介面的物件自動獲得驗證服務。切面 AccountOperationValidationServiceInjector 持有服務參考,於客戶端建立時注入:

public aspect AccountOperationValidationServiceInjector {
    private AccountOperationValidationService service;
    public void setService(AccountOperationValidationService aService) { this.service = aService; }
    pointcut clientCreation(AccountOperationValidationClient aClient) :
        initialization(AccountOperationValidationClient+.new(..)) && this(aClient);
    after(AccountOperationValidationClient aClient) returning : clientCreation(aClient) {
        aClient.setAccountOperationValidationService(this.service);
    }
}

雙層控制:服務定義於 Spring XML,客戶端僅需實作介面即可自動配置。

重複注入:動態依賴更新

當客戶端需在執行期動態切換策略(如報價策略),可採用欄位層級注入。切面攔截欄位 get 連接點,每次存取皆回傳最新策略實例:

public aspect PricingStrategyInjector {
    private PricingStrategy currentPricingStrategy;
    public void setCurrentPricingStrategy(PricingStrategy aStrategy) { this.currentPricingStrategy = aStrategy; }
    pointcut pricingStrategyAccess() : get(PricingStrategy *) && !within(PricingStrategyInjector);
    PricingStrategy around() : pricingStrategyAccess() { return this.currentPricingStrategy; }
}

替代方案為服務定位器介面,犧牲些許程式碼簡潔度換取清晰度。

結語

本文將 DI 視為物件與執行環境間的契約,詳述四大代理職責與多種滿足技術。優先讓 Spring 容器管理物件生命週期;對於容器外物件,@SpringConfigured 註解提供優雅解決方案,將配置資訊完全外部化。完整範例代碼可至 IBM developerWorks 下載。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved