kooyee ‘s blog

          開源軟件, 眾人努力的結晶, 全人類的共同財富
          posts - 103, comments - 55, trackbacks - 0, articles - 66
             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          [WEB] MetaData Programme

          Posted on 2007-12-01 20:36 kooyee 閱讀(754) 評論(0)  編輯  收藏 所屬分類: Java

          MetaData Programme

          1.1.        什么是元數據編程

          什么是元數據,元數據就是描述數據的數據(data about data)。最明顯的例子是XML Schemaxml schema就是描述xml的數據,所以它是元數據。另一個例子是數據庫,比如我們可以查詢數據庫中有幾個表,每個表都有什么字段,這些數據就是元數據。Office:office" />

          在開發的世界里,元數據就是能夠綁定到一個類的附加信息,在靜態或者運行時間。JCR175給我們提供annotation就是一種元數據。

          不過在這之前一個我們已經廣泛使用的元數據是XML,如就是EJBXML發布描述符中,你需要定義基于每一個方法的事務屬性。應用服務器指導什么時候,什么地方開始,掛起或者提交一個事務,因為你在BEANXML的配置文件中的元數據內已經定義如方法:RequiredRequiresNewSupport等等,它們綁定在你的EJB類和事務管理之間。XDoclet是另一個元數據的例子。

          1.2.        Annotation的意義和簡單例子

                 JDK1.5提供的annotation與我們所常見的classesfieldssmethods間是什么關系。如下:如果說類和數據成員是名詞,方法是動詞,那么annotation就是形容詞或者副詞,分別描述它們的所具有屬性。

             好,現在就來實現一個annotation

           

          import Java.lang.annotation.Retention;

          package sample.annotation;

          @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)

              
          public @interface Broker {

              String name();

              String address();

              }


          }

           

          使用這個annotation

          Import sample.annotation.broker;

          @Broker(name
          ="anders", address="xiamen")

          public class Agent {

              
          public String getTelPhone (){

                  
          return "010-0592-2519280";

              }


          }


          運行期得到這個annotation

          public class Main {

              
          public static void main(String[] args){

          Agent agent 
          = new Agent();

                  
          try{

                      Annotation[] a 
          = agent.getClass().getMethod("getBrokerName").getAnnotations();

                      
          for (int i=0; i<a.length ; i++{

                           
          if( a[i] instanceof Broker){

                                Broker broker 
          = (Broker)a[i];

                               System.out.println(broker.name());

                           }


                       }


                  }


                  
          catch(Exception e){

                      e.printStackTrace(System.out);

                  }


             }


          }




          1.3.
                  Annotationclass文件格式

                 利用sun公司的提供的javap,我們可以看到annotation的在class文件中的表示。以下為對比結果:

          源碼

           

          @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)

          public @interface Broker {

              String name();

              String address();

          }

           

          Javap結果:

          Compiled from "Broker.java"

          interface Broker extends java.lang.annotation.Annotation

           SourceFile: "Broker.java"

           minor version: 0

           major version: 0

           Constant pool:

          const #1 = class    #9; // Broker

          const #2 = class    #10; // Object

          const #3 = class    #11; //Annotation

          const #4 = Asciz    name;

          const #5 = Asciz   ()Ljava/lang/String;;

          const #6 = Asciz    address;

          const #7 = Asciz    SourceFile;

          const #8 = Asciz    Broker.java;

          const #9 = Asciz    Broker;

          const #10 = Asciz   java/lang/Object;

          const #11 = Asciz   java/lang/annotation/Annotation;

          {

          public abstract java.lang.String name();

          public abstract java.lang.String address();

          }  

          源碼:

          @Broker(name="anders", address="xiamen")

          public class Agent {

              
          public String getTelPhone(){

                  
          return "0592-2519580";

              }


          }


          Javap結果:

          Compiled from "Agent.java"

          public class Agent extends java.lang.Object

           SourceFile: "Agent.java"

           RuntimeVisibleAnnotations: length = 0x10

             00 01 00 11 00 02 00 12 73 00 13 00 14 73 00 15

            

           minor version: 0

           major version: 0

           Constant pool:

          const #1 = Method   #4.#22;// java/lang/Object."<init>":()V

          const #2 = String   #23;    // 0592-2519580

          const #3 = class    #24;    // Agent

          const #4 = class    #25;    // Object

          const #5 = Asciz    <init>;

          const #6 = Asciz    ()V;

          const #7 = Asciz    Code;

          const #8 = Asciz    LineNumberTable;

          const #9 = Asciz    LocalVariableTable;

          const #10 = Asciz   this;

          const #11 = Asciz   LAgent;;

          const #12 = Asciz   getTelPhone;

          const #13 = Asciz   ()Ljava/lang/String;;

          const #14 = Asciz   SourceFile;

          const #15 = Asciz   Agent.java;

          const #16 = Asciz   RuntimeVisibleAnnotations;

          const #17 = Asciz   LBroker;;

          const #18 = Asciz   name;

          const #19 = Asciz   anders;

          const #20 = Asciz   address;

          const #21 = Asciz   xiamen;

          const #22 = NameAndType#5:#6;// "<init>":()V

          const #23 = Asciz   0592-2519580;

          const #24 = Asciz   Agent;

          const #25 = Asciz   java/lang/Object;

          // 以下為方法域,略  

          補充說明:我們都知道在java 1.0發布時,java class file的格式就已經定下來,要說明的是為了應未來的需要java class file設計了屬性說的機制。一直到J2SE1.4都沒有怎么改變。但這次為了更好的支持metadata技術,一共增加了8個屬性。分別是:

          EnclosingMethodAttribute Anonymous Class Local Inner Class 使用此 Attribute 描述該Class Scope

          Signature AttributeGenerics ClassMethod、或 Filed使用此 Attribute 來記錄所要的類型,因為java的范型采用了擦拭法。

          LocalVariableTypeTable Attribute:主要是給 debugger 使用,目的和「LocalVariableTable Attribute類似,只是「LocalVariableTable Attribute 記錄所要的參數表,而「LocalVariableTypeTable Attribute 記錄參數的類型。

          RuntimeVisibleAnnotations Attribute:確定該annotation可以被reflectionAPI返回,適用對象:ClassMethodField

          RuntimeInvisibleAnnotations Attribute:確定該annotation無法被reflectionAPI返回,適用對象: ClassMethodField 

          RuntimeVisibleParameterAnnotations Attribute:同「RuntimeVisibleAnnotations Attribute,適用對象:Method,(該Method 的參數

          RuntimeInvisibleParameterAnnotations Attribute:同「RuntimeInvisibleAnnotations Attribute,適用對象:Method,(該Method 的參數。

          AnnotationDefault Attribute:適用對象:Method,記錄默認值。

          1.4.        為什么需要Annotation

                 annotation之前我們已經廣泛使用另外一種元數據xml,為什么需要annotationAnnotationxml的作為元數據的區別是什么——位置。Annotation寫在源代碼中,而xml寫在外部。

                 為什么要這樣?如果你開發EJB,你一定為你的EJB寫過xml描述文件。當大量的EJB需要描述時,就出現了所謂的"descriptor hell"。這個也導致了著名的XDoclet的出現。而annotation出現可以避免這種descriptor hell。另外你更改了某個方法為其增加或者減少一個參數,你就對應的修改xml文件,而使用annotation則不必。使用annotation將開發和部署更方便,提供開發效率。

                 另外:使用xml的另一個問題是:很多Xml配置太過verbose。相比較EJBHibernate 或者Webwork可以明顯的發現不同。

          1.5.        再議Annotation

          EJB3中,Annotation把開發和部署的工作合在一起。但是在一些企業環境中,開發人員并不控制諸如數據源名等(這些是部署部門和管理部門的工作),這樣把數據源名寫在xml中將比較好。

                 Annotation是本身靜態的,一旦添加或者修改annotation都需要重新編譯,在運行時讀取,這樣就喪失了運行時配置的能力。因此Annotations 不會取代xml,它只是提供了另一種途徑。并且我相信sun公司將在未來提供一個方式可以在運行期更改metadata

          關于這點TSS上有著很激烈的討論,很多開發人員提出:利用xml來更改annotation,并希望類似的方式能被采納為標準規范。比如使用如下格式:

           

          <class name="org.hibernate.Item">

          @Entity

              @Table(name="AUCTION_ITEM")

              
          <method sig="getBar()">@Transient</method>

              
          <method sig="doSomething(int, String)">

                  @Tx(REQUIRES_NEW)

              
          </method>

          </class>

           

           當然也有不同意見:But then, I think of "overriding" as a different problem to "xml deployment descriptors", and so I think we need two solutions. I think Cedric and Bill are trying to kill two birds with one stone, so maybe their proposals are better....

          關于為annotation提供動態配置能力的問題,其中一個網友認為:Sun make it real pain to do the deployment XML so that they can introduce annotation to fix it. The annotation can make code/deployment coupling so strong that Sun can come out with a new way (annotation interceptor in jdk 1.6? :)) for fixing it. and the cycles goes on...這讓我想起了類似的現象:JSPTagLib。希望Annotation不會和TagLib有同樣的命運。

                 Annotation本身引入另一種類型的接口。在EJB3中確實使程序更加POJO,也消除了一些接口。并且編譯后的代碼也可以直接移植到另一個并不處理這些annotations的環境中(感謝VM在加載類時并不檢查那些annotationsclasses,甚至這些類不在classpath中)。然而代碼也確實增加了另一些接口。這個表現在編譯期,如果沒有這些annotation classes,是編譯不過的。

                 另一個問題(還好不算很重要),關于annotationnamespace。在多層應用的系統中,可能會出現同樣的全局annotationnamespace沖突。比如一些公共的annotation,如@Transaction,將會被應用在很多個層次中。盡量讓namespace長些,可以避免這個問題。

          1.6.        元數據編程的應用:

          Annotation已經被集成到很多的java規范和標準中,很重要的是它已經被J2EE標準,如EJB3所采用,當然也被許多開源的組件體系如:ASPectJ

                 Annotation最重要的應用將是AOP:由于annotation可以天然的表示系統中的另一個橫切面,同時Annotation的識別是通過反射得到的,所以Annotation很自然的應用到基于動態代理的AOP實現。AOPAlliance也支持metadata handlingAspectJ也發布了基于annotation的新版本。

                 在實現AOP上,使用annotation也比使用XML有一個優勢:如前所述,annotation更像是形容詞和副詞,這樣比較不容易verbose。當然這個是相對的,在實際的實現中更依賴開發人員的努力。

                 這里,筆者將展示一個不完整也不成熟的基于annotationAOP例子代碼——關于銀行卡的例子。

                 功能需求:銀行卡業務分為轉帳,查詢余額,查詢明細,POS消費等。這其中轉帳和POS消費是要收費的(轉帳收取的是用戶的手續費,而POS消費收取的是商家的手續費),另外POS消費還可能有積分的(比如筆者的牡丹貸記卡)。消費轉帳都要記錄明細。但查詢余額就不需要記錄在明細中。

                 代碼如下(在這個例子沒有用動態代理也沒有用已有的AOP框架,使代碼看起來簡單些)

            1// 銀行卡對象
            2
            3public class Card {
            4
            5    private String account; 
            6
            7//some field method
            8
            9}

           10
           11Annotation:
           12
           13// 手續費
           14
           15// type= "user", 表示收取用戶手續費; type= "Biz", 表示收取商家手續費
           16
           17public @interface Fee{
           18
           19    String type();
           20
           21}

           22
           23// 積分
           24
           25public @interface Index {
           26
           27}

           28
           29// 記錄明細
           30
           31public @interface BizLog {
           32
           33}

           34
           35// 事務處理
           36
           37public @interface Transaction {
           38
           39}

           40
           41// 業務接口
           42
           43public interface BizAction {
           44
           45    void execute(Card card, RunData rundata);
           46
           47}

           48
           49// 轉帳業務
           50
           51@Fee(type="user")
           52
           53@Transaction
           54
           55@BizLog
           56
           57public class TransferAction implements BizAction {
           58
           59    public void execute(Card card, RunData rundata) {
           60
           61        //To change body of implemented methods use File | Settings | File Templates.
           62
           63    }

           64
           65}

           66
           67// POS消費
           68
           69@Fee(type="Biz")
           70
           71@Transaction
           72
           73@BizLog
           74
           75@Index
           76
           77public class POSAction implements BizAction {
           78
           79    public void execute(Card card, RunData rundata) {
           80
           81        //To change body of implemented methods use File | Settings | File Templates.
           82
           83    }

           84
           85}

           86
           87// 查詢明細
           88
           89public class QueryDetail implements BizAction {    
           90
           91    public void execute(Card card, RunData rundata) {
           92
           93        //To change body of implemented methods use File | Settings | File Templates.
           94
           95    }

           96
           97}

           98
           99// 業務操作監視器接口
          100
          101public interface BizActionMonitor {
          102
          103   void execute(BizAction action, RunData rundata);
          104
          105}

          106
          107// 業務操作監視器實現
          108
          109public class BizActionMonitorImpl implements BizActionMonitor{
          110
          111    public void execute(BizAction action, RunData rundata) {
          112
          113        Annotation[] annotations = action.getClass().getAnnotations();
          114
          115        for(Annotation annotation : annotations){
          116
          117            if (annotation instanceof Fee)// 計算手續費   }
          118
          119            if (annotation instanceof Index)//計算積分    }
          120
          121            if (annotation instanceof Transaction)// 準備事務 }
          122
          123            if (annotation instanceof BizLog)// 記錄明細   }
          124
          125        }

          126
          127    }

          128
          129}

          130
          131// 控制器對象
          132
          133public class controller{
          134
          135    private BizActionMonitor monitor;   
          136
          137    public void execute(BizActionUI, rundata){
          138
          139        BizAction action = getAction(BizActionUI);      
          140
          141        monitor.execute(action, rundata);
          142
          143    }

          144
          145}

          146
          147// 運行時數據
          148
          149public interface RunData {
          150
          151   // some method
          152
          153}

          154
          155// 用戶設備(POS機, ATM或者柜臺終端)接口
          156
          157public class BizActionUI {
          158
          159      private RunData rundata;
          160
          161      private Controller controller;
          162
          163      public BizActionUI(RunData rundata, Controller controller){
          164
          165          this.rundata = rundata;
          166
          167          this.controller = controller;
          168
          169      }

          170
          171      public void execute()// 某個子類實現 }
          172
          173      public void commit(){
          174
          175          controller.execute(this, rundata);
          176
          177      }

          178
          179}

          180
          181public class Main{
          182
          183    private Rundata rundata;
          184
          185    private Controller controller; 
          186
          187    public populateUI(command){
          188
          189        BizActionUI ui = getUI(command);
          190
          191        ui.execute();
          192
          193    }
             
          194
          195    public BizActionUI getUI(command){
          196
          197        //
          198
          199        BizActionUI ui 
          200
          201        if//.){
          202
          203            ui = new SomeBizActionUI(rundata, controller);
          204
          205        }

          206
          207        return ui;      
          208
          209    }
              
          210
          211    public static main(String[] args){
          212
          213        //
          214
          215        Main main = new Main();
          216
          217        main.populateUI(command)
          218
          219        //
          220
          221    }

          222
          223}

          224
          225

          1.7.
                  結束語:

          本文討論了annotation技術,展示了annotationclass文件格式,并討論了annotation技術本身的優勢和不足,并于現有的xml技術加以比較,展望了annotation技術的應用前景AOP

          限于筆者自身的水平(包括技術水平和寫作水平),技術上對annotation的學習比較有限,寫作上也感覺好多話無法用文字來表達,因而本文的代碼會比較多(大概有一半以上)。

          主站蜘蛛池模板: 成武县| 鄄城县| 红桥区| 衡阳县| 兴隆县| 本溪市| 桑植县| 仁化县| 舞阳县| 漯河市| 巴彦淖尔市| 绵阳市| 梁山县| 衡南县| 景谷| 雅江县| 万载县| 通化县| 仁寿县| 平湖市| 南溪县| 邮箱| 乐平市| 龙州县| 朝阳区| 专栏| 临颍县| 陕西省| 文水县| 丰县| 许昌市| 汝南县| 安丘市| 苍山县| 安溪县| 濉溪县| 盖州市| 洛隆县| 贵州省| 万安县| 军事|