熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

用Java Annotations管理對象生命周期

2022-06-13   來源: Java高級技術 
摘要

  Java Annotations主要用來標注deprecated的代碼在這篇文章中它們用來把方法調用的控制權移交給一個輕量級框架中負責處理一系列方法調用的組件因此正確的初始化和設置等操作被委派給客戶端應用而不是類以做到設置和控制都可以調整
 
  對於開發者來說復雜的應用通常有很多初始化問題需要處理許多不同的步驟無非是建立面板配置服務之類而這些事情的難點在於有一些步驟需要重復另一些則不需要把這種管理問題交給類自己處理是非常麻煩的因為邏輯可能會變化另外現代軟件設計強調分離職責簡單來說我們的目的是把做什麼和怎麼做分離開來

  這篇文章展示給大家如何使用 Annotations來做初始化控制這種做法超越了簡單的標注它介紹了一個小的API可以用它來開發你自己的phaseable Annotations或者在這種新特性上給你提供一些靈感

  Annotations

  Annotations是JSE 引入的新語言特性通常 Annotations允許開發者用一種跟運行代碼無關的次要信息來標注類方法以及成員這樣就可以使用類似評價的 Annotations比如好方法壞方法或者更詳細一些不推薦的方法覆寫的方法這些用法的可能性是無窮的不過請注意方法或類跟標注實際可能不相關比如不推薦的如果想知道關於 Annotations的更多詳細討論請閱讀Java Tiger: A Developers Notebook

  因為 Annotations可以用來描述用例或者實體比如方法和類的意思所以這是一種語法棒棒糖反過來這些附加信息也可以被其他東西(比如框架)用於各種各樣的動作比如生成文檔(Javadoc)或者像這裡討論的作為一種特殊內容來控制行為比如對象的生命周期

  生命周期管理

  生命周期管理通常發生在中間件環境中比如應用服務器這種思想是把對象的創建使用以及銷毀跟對象本身分開例如在一個發布不同服務的應用服務器中它通常不關心所請求的特殊服務(譯注此處的意思應該是應用服務器對所有請求都一視同仁)調用服務的機制或多或少的采用了同一種方案這取決於應用的狀態呼叫者以及其他參數一些必要的變量但是在一個易於管理的環境中基本的算法通常是一系列操作的順序鏈在Java客戶端應用中必須處理mask的顯示或者form允許用戶輸入或修改數據

 示例問題

  在Java應用中mask通常用於數據收集以及在CRUD(create read update delete)周期中處理數據用戶可以修改刪除或者新增加一些數據跟一個簡單的商務問題一樣我們需要管理在客戶端應用中如何顯示mask這樣我們把顯示從操作鏈中分離了出來像下面這樣
創建mask在這一狀態中最好只安排一次
初始化在這一狀態數據從文件和數據庫等地方找回並填充到mask的字段中
激活這裡用戶放棄對mask的控制
  
  在現實中涉及到很多方面訪問驗證控制依賴等等

  Phases

  在這篇討論中我提到了每一步操作的phase基本思想非常簡單我們把類方法標注成操作鏈中的phases然後把這些方法的調用交給服務(框架)來做實際上這種方法並不僅限於生命周期管理它可以用做商務流程中所有調用的控制機制

  我們使用的 Annotations簡單的命名為Phase我們使用它來把一個方法標注成操作鏈的一部分在下面的代碼裡你可以看到 Annotations的聲明與接口很接近

  @Retention(RetentionPolicyRUNTIME)
@Target({ElementTypeMETHOD})
public @interface Phase {
   int index();
   String displayName() default ;
}



  我們簡單看一下代碼在頭兩行你看到 Annotations跟另外兩個 Annotations一起使用剛看上去時這有點混亂但是這兩行很簡單的就指定了 AnnotationsPhase只允許並且應該保留到編譯後之所以增加這兩個 Annotations是因為有些 Annotations可能只會在編譯期間被使用並且可能指向類或者成員

  @interface是一個 Annotations的標准描述在接下來的代碼中index和displayName——不只聲明了一個成員還聲明了一個方法——也是Java的新語法如果沒提供初始值的話 displayName將被賦予了一個空字符串作為初始值同時這個displayName能夠被用來作為監測用途叫做progress bar index()是必須的它告訴框架這些phase可以被缺省的執行

  像我早先說的那樣我們應該把這個邏輯從對象中分離出來所以我們定義了一個必須實現的接口以用於調用管理這個接口可以被一個客戶端對象實現為了達到管理的目的我們定義了一個通用的標記接口所有的phaseable接口必須從這裡繼承這樣框架就可以通過一個唯一的訪問點來管理類

  public interface Phased {
}



  這個接口的具體實現會看起來像下面的代碼那樣這裡接口定義了一個mask或者一個form它們包含幾個操作這些操作必須像上面的描述那樣被定義

  public interface PhasedMask extends Phased {

   @Phase(index=)
   public void construct();

   @Phase(index=)
   public void initialize();

   @Phase(index=displayName=Activating)
   public void activate();

}



  你可以看到如何使用 Annotations它寫在方法聲明之前並使用一個介紹性的@sign它的屬性index需要提供圓括號請注意因為 Annotations並不是一個Java聲明所以結尾不能出現分號現在我們需要一個類來來把這些東西聯結起來並且試試我們剛才定義的phase

  Phaser

  主要處理類也許應該被稱為Phaser(喂我們不都挺喜歡星際旅行嗎?)它執行全部的phase並且為用戶提供簡單的監視機制這個類的實現並不包含在這篇文章裡當然你可以從資源找到框架代碼的下載

  一個Phaser擁有一個實現了一些具體的PhasedXxxx接口並且管理phase調用的對象
假設我們有一個像這樣的MyMask類

  public class MyMask implements PhasedMask {

   @Phase(index = )
   public void construct() {
      // Do the layout
   }

   @Phase(index = )
   public void initialize() {
      // Fill the mask with data
   }

   @Phase(index = )
   public void activate() {
      // Activate the listeners and allow the user to interact with the mask
   }
  
   // Business code

}



  現在我們可以像下面那樣調用這些PhasedMask方法

  Phaser phaser = new Phaser( phasedMask );
phaserinvokeAll();



  這樣方法construct()initialize()和activate()就都被調用了

  那麼我們如何控制phase呢?我們跳過構造階段因為當我們第二次調用phasedMask()時並不需要再布置一次本意是我們不需要construct()被調用多次因為我們把這個方法用索引標注所以我們可以簡單的跳過這個索引並且告訴Phaser應該執行哪些phase

  Phaser phaser = new Phaser( phasedMask );
phaserinvoke( new int[] {} );



  這樣就好了不過不夠清晰誰會記得phase的索引實際代表什麼?幸運的是我們可以像下面這樣寫得詳細一點

  Phaser phaser = new Phaser( phasedMask );
phaserinvoke( new String[] {initializeactivate} );



  這裡我們使用從接口繼承來的方法名當然如果需要的話我們可以重新安排phase所以為了交換順序我們可以這樣寫

  Phaser phaser = new Phaser( phasedMask );
phaserinvoke( new String[] {activateinitialize} );



  這個好象沒什麼意義但是當某個設置中一些phase是緊耦合的時這種做法是有用的

  因為我們在這裡通過反射來調用方法所以存在很多拋出異常的情況Phaser會捕捉這些異常並包裝成所謂的PhaserException所以如果一個方法調用失敗(比如是私有的)Phaser的invoke()方法會拋出一個包含著最初異常的PhaseException如果對反射知之不多請看邊欄的Notes on Reflection

  你也許會給Phaser增加一個PhaseListener來觀察裡面發生了什麼並在漫長的phase調用過程中反饋給用戶一些信息

  PhaseListener listener = new PhaseListener() {
   public void before( PhaseEvent e ) {
      // This is called before the Phaser invokes a phase
   }
   public void after( PhaseEvent e ) {
      // This is called after the Phaser has successfully invoked a phase
   }
};

Phaser phaser = new Phaser( phasedMask );
phaseraddPhaseListener( listener );
phaserinvoke( new String[] {initializeactivate} );

    討論和總結

  在這篇文章中你看到了如何利用 Annotations來管理被分成幾個phase的類的生存周期為了使這些類能夠被框架組件所管理它們必須簡單的實現一個接口這個接口從Phased派生而來並且用Phase Annotations標注了方法管理通過Phaser類來完成這個類能夠調用被標注方法並能控制調用的順序還提供了一種事件處理機制來觀察Phaser的工作

  這種方法也顯示了一種比Javadoc的 Annotations使用更進一步的用法它們不只用於生命周期管理也可以用於常規的對象初始化

  實現類不關心它們的方法被調用的順序如果你在設計中保持這種思想你就可以更靈活的使用這些類

  如果phase必須重新排列或者忽略這些行為會發生在實現類中

  像任何工具一樣它有一些缺點如果接口必須改變或者新接口必須保持向後兼容性以保證源代碼完全可用那麼實現類必須改變這種方案缺少參數和返回值的支持參數必須在phase調用前被完全提供同樣因為大量使用了反射所以會成為一個高性能要求的系統中的瓶頸

  最後調用鏈對IDE來說是不明晰的換句話說[請把你最喜歡的JavaIDE列出來]不可能顯示給開發者在編譯時什麼方法在哪裡被調用了

  關於作者
Norbert Ehreke是Impetus Unternehmensberatung GmbH旗下一名高級開發者這家咨詢公司位於德國的法蘭克福他負責框架開發並在PerlJavaC#上都有所涉獵他曾經就學於德國柏林的Technical University (TU)德國馬裡蘭的University of Maryland以及瑞士蘇黎世的Eidgenoessische Technische Hochschule (ETH)他從柏林的TU獲得了系統工程碩士學位他的興趣包括面向對象編程(Java Perl C#)面向切面編程(AOP)以及最近的面向語言編程(LOP)在空閒時他喜歡山地自行車烹饪和電影

  資源
Javaworld:
Matrix:
下載這篇文章的源代碼
/annotations/jwannotationszip
獲得Phaser框架(抱歉Javadoc現在只有德文版的我會盡快把文檔本地化的


  關於反射的筆記
  Java提供了在運行時分析類的能力你可以獲取一個類指出它的成員方法以及它實現的借口你也可以找出這些的參數也就是一個方法的參數類型返回類型以及方法和成員的名字並且你可以訪問你找到的成員也就是你可以調用方法操作公有成員等等

  這是一種非常強大的能力因為我們可以處理編譯時我們沒引入的類這種Java特性是我在這篇文章中討論的框架類的基礎


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27452.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.