| 導購 | 订阅 | 在线投稿
分享
 
 
 

使用Java Annotations來管理對象生命周期

來源:互聯網  2008-06-01 02:59:26  評論

Java Annotations主要用來標注deprecated的代碼。在這篇文章中,它們用來把方法調用的控制權移交給一個輕量級框架中負責處理一系列方法調用的組件。因此,正確的初始化和設置等操作被委派給客戶端應用而不是類,以做到設置和控制都可以調整。

對于開發者來說,複雜的應用通常有很多初始化問題需要處理。許多不同的步驟無非是建立面板,配置服務之類。而這些事情的難點在于,有一些步驟需要重複,另一些則不需要。把這種管理問題交給類自己處理是非常麻煩的,因爲邏輯可能會變化。另外,現代軟件設計強調分離職責。簡單來說,我們的目的是把做什麽和怎麽做分離開來。

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

版權聲明:任何獲得Matrix授權的網站,轉載時請務必保留以下作者信息和鏈接

作者:Norbert Ehreke;deafwolf(作者的blog:http://blog.matrix.org.cn/page/deafwolf)

原文:http://www.matrix.org.cn/resource/article/44/44403_Java+Annotations.html

關鍵字:Java;Annotations

Annotations

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

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

生命周期管理

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

示例問題

在Java應用中,mask通常用于數據收集以及在CRUD(create, read, update, delete)周期中處理數據。用戶可以修改、刪除或者新增加一些數據。跟一個簡單的商務問題一樣,我們需要管理在客戶端應用中如何顯示mask。這樣,我們把顯示從操作鏈中分離了出來,像下面這樣:

1.創建:mask在這一狀態中最好只安排一次。

2.初始化:在這一狀態,數據從文件和數據庫等地方找回,並填充到mask的字段中。

3.激活:這裏,用戶放棄對mask的控制。

在現實中,涉及到很多方面:訪問、驗證、控制依賴等等。

Phases

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

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

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

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=0)

public void construct();

@Phase(index=1)

public void initialize();

@Phase(index=2,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 = 0)

public void construct() {

// Do the layout }

@Phase(index = 1)

public void initialize() {

// Fill the mask with data }

@Phase(index = 2)

public void activate() {

// Activate the listeners and allow the user to interact with the mask }

// Business code}

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

Phaser phaser = new Phaser( phasedMask );phaser.invokeAll();

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

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

Phaser phaser = new Phaser( phasedMask );phaser.invoke( new int[] {1,2} );

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

Phaser phaser = new Phaser( phasedMask );

phaser.invoke( new String[] {"initialize","activate"} );

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

Phaser phaser = new Phaser( phasedMask );

phaser.invoke( new String[] {"activate","initialize"} );

這個好象沒什麽意義,但是,當某個設置中一些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 );phaser.addPhaseListener( listener );

phaser.invoke( new String[] {"initialize","activate"} );

討論和總結

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

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

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

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

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

最後,調用鏈對IDE來說是不明晰的。

Java Annotations主要用來標注deprecated的代碼。在這篇文章中,它們用來把方法調用的控制權移交給一個輕量級框架中負責處理一系列方法調用的組件。因此,正確的初始化和設置等操作被委派給客戶端應用而不是類,以做到設置和控制都可以調整。 對于開發者來說,複雜的應用通常有很多初始化問題需要處理。許多不同的步驟無非是建立面板,配置服務之類。而這些事情的難點在于,有一些步驟需要重複,另一些則不需要。把這種管理問題交給類自己處理是非常麻煩的,因爲邏輯可能會變化。另外,現代軟件設計強調分離職責。簡單來說,我們的目的是把做什麽和怎麽做分離開來。 這篇文章展示給大家如何使用 Annotations來做初始化控制,這種做法超越了簡單的標注。它介紹了一個小的API,可以用它來開發你自己的「phaseable」 Annotations,或者在這種新特性上給你提供一些靈感。 版權聲明:任何獲得Matrix授權的網站,轉載時請務必保留以下作者信息和鏈接 作者:Norbert Ehreke;deafwolf(作者的blog:http://blog.matrix.org.cn/page/deafwolf) 原文:http://www.matrix.org.cn/resource/article/44/44403_Java+Annotations.html 關鍵字:Java;Annotations Annotations Annotations是J2SE 5.0引入的新語言特性。通常, Annotations允許開發者用一種跟運行代碼無關的次要信息來標注類,方法以及成員。這樣就可以使用類似評價的 Annotations,比如「好方法」、「壞方法」,或者更詳細一些,「不推薦的方法」、「覆寫的方法」。這些用法的可能性是無窮的。不過請注意,方法或類跟標注實際可能不相關,比如「不推薦的」。如果想知道關于 Annotations的更多詳細討論,請閱讀Java 5.0 Tiger: A Developer's Notebook。 因爲 Annotations可以用來描述用例或者實體比如方法和類的意思,所以這是一種語法棒棒糖。反過來,這些附加信息也可以被其他東西(比如框架)用于各種各樣的動作,比如生成文檔(Javadoc),或者像這裏討論的,作爲一種特殊內容來控制行爲,比如對象的生命周期。 生命周期管理 生命周期管理通常發生在中間件環境中,比如應用服務器。這種思想是把對象的創建、使用以及銷毀跟對象本身分開。例如在一個發布不同服務的應用服務器中,它通常不關心所請求的特殊服務(譯注:此處的意思應該是應用服務器對所有請求都一視同仁),調用服務的機制或多或少的采用了同一種方案。這取決于應用的狀態,呼叫者以及其他參數,一些必要的變量,但是在一個易于管理的環境中,基本的算法通常是一系列操作的順序鏈。在Java客戶端應用中,必須處理mask的顯示,或者form允許用戶輸入或修改數據。 示例問題 在Java應用中,mask通常用于數據收集以及在CRUD(create, read, update, delete)周期中處理數據。用戶可以修改、刪除或者新增加一些數據。跟一個簡單的商務問題一樣,我們需要管理在客戶端應用中如何顯示mask。這樣,我們把顯示從操作鏈中分離了出來,像下面這樣: 1.創建:mask在這一狀態中最好只安排一次。 2.初始化:在這一狀態,數據從文件和數據庫等地方找回,並填充到mask的字段中。 3.激活:這裏,用戶放棄對mask的控制。 在現實中,涉及到很多方面:訪問、驗證、控制依賴等等。 Phases 在這篇討論中,我提到了每一步操作的phase,基本思想非常簡單:我們把類方法標注成操作鏈中的phases,然後把這些方法的調用交給服務(框架)來做。實際上,這種方法並不僅限于生命周期管理。它可以用做商務流程中所有調用的控制機制。 我們使用的 Annotations簡單的命名爲Phase,我們使用它來把一個方法標注成操作鏈的一部分。在下面的代碼裏,你可以看到 Annotations的聲明與接口很接近。 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) 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=0) public void construct(); @Phase(index=1) public void initialize(); @Phase(index=2,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 = 0) public void construct() { // Do the layout } @Phase(index = 1) public void initialize() { // Fill the mask with data } @Phase(index = 2) public void activate() { // Activate the listeners and allow the user to interact with the mask } // Business code} 現在,我們可以像下面那樣調用這些PhasedMask方法: Phaser phaser = new Phaser( phasedMask );phaser.invokeAll(); 這樣,方法construct()、initialize()和activate()就都被調用了。 那麽我們如何控制phase呢?我們跳過構造階段,因爲當我們第二次調用phasedMask()時,並不需要再布置一次。本意是我們不需要construct()被調用多次。因爲我們把這個方法用0索引標注,所以我們可以簡單的跳過這個索引,並且告訴Phaser應該執行哪些phase。 Phaser phaser = new Phaser( phasedMask );phaser.invoke( new int[] {1,2} ); 這樣就好了,不過不夠清晰。誰會記得phase的索引實際代表什麽?幸運的是,我們可以像下面這樣寫得詳細一點: Phaser phaser = new Phaser( phasedMask ); phaser.invoke( new String[] {"initialize","activate"} ); 這裏,我們使用從接口繼承來的方法名。當然,如果需要的話,我們可以重新安排phase。所以,爲了交換順序,我們可以這樣寫: Phaser phaser = new Phaser( phasedMask ); phaser.invoke( new String[] {"activate","initialize"} ); 這個好象沒什麽意義,但是,當某個設置中一些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 );phaser.addPhaseListener( listener ); phaser.invoke( new String[] {"initialize","activate"} ); 討論和總結 在這篇文章中,你看到了如何利用 Annotations來管理被分成幾個phase的類的生存周期。爲了使這些類能夠被框架組件所管理,它們必須簡單的實現一個接口,這個接口從Phased派生而來,並且用Phase Annotations標注了方法。管理通過Phaser類來完成,這個類能夠調用被標注方法,並能控制調用的順序,還提供了一種事件處理機制來觀察Phaser的工作。 這種方法也顯示了一種比Javadoc的 Annotations使用更進一步的用法。它們不只用于生命周期管理,也可以用于常規的對象初始化。 實現類不關心它們的方法被調用的順序。如果你在設計中保持這種思想,你就可以更靈活的使用這些類。 如果phase必須重新排列或者忽略,這些行爲會發生在實現類中。 像任何工具一樣,它有一些缺點。如果接口必須改變,或者新接口必須保持向後兼容性以保證源代碼完全可用,那麽實現類必須改變。這種方案缺少參數和返回值的支持。參數必須在phase調用前被完全提供。同樣,因爲大量使用了反射,所以會成爲一個高性能要求的系統中的瓶頸。 最後,調用鏈對IDE來說是不明晰的。
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
王朝網路微信公眾號
微信掃碼關註本站公眾號 wangchaonetcn
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有