摘要
Java Annotations主要用來標注deprecated的代碼
在這篇文章中
它們用來把方法調用的控制權移交給一個輕量級框架中負責處理一系列方法調用的組件
因此
正確的初始化和設置等操作被委派給客戶端應用而不是類
以做到設置和控制都可以調整
對於開發者來說
復雜的應用通常有很多初始化問題需要處理
許多不同的步驟無非是建立面板
配置服務之類
而這些事情的難點在於
有一些步驟需要重復
另一些則不需要
把這種管理問題交給類自己處理是非常麻煩的
因為邏輯可能會變化
另外
現代軟件設計強調分離職責
簡單來說
我們的目的是把做什麼和怎麼做分離開來
這篇文章展示給大家如何使用 Annotations來做初始化控制
這種做法超越了簡單的標注
它介紹了一個小的API
可以用它來開發你自己的
phaseable
Annotations
或者在這種新特性上給你提供一些靈感
Annotations Annotations是J
SE
引入的新語言特性
通常
Annotations允許開發者用一種跟運行代碼無關的次要信息來標注類
方法以及成員
這樣就可以使用類似評價的 Annotations
比如
好方法
壞方法
或者更詳細一些
不推薦的方法
覆寫的方法
這些用法的可能性是無窮的
不過請注意
方法或類跟標注實際可能不相關
比如
不推薦的
如果想知道關於 Annotations的更多詳細討論
請閱讀Java
Tiger: A Developer
s 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