<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    Java Annotation 高級應用

    版權聲明:本文可以自由轉(zhuǎn)載,轉(zhuǎn)載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
    作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig)
    原文:http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html
    關鍵字:java,annotation,apt

    前言:
    前不久在matrix上先后發(fā)表了《java annotation 入門》《java annotation 手冊》兩篇文章,比較全面的對java annotation的語法、原理、使用三方面進行了闡述。由于《入門》中的簡單例程雖然簡單明了的說明了annotation用法,但給大家的感覺可能是意猶未見,所以在此行文《java annotation高級應用》,具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。

    一、摘要:
    《java annotation高級應用》具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。本文列舉了用于演示annotation的BRFW演示框架、演示APT的apt代碼實例,并對其進行較為深度的分析,希望大家多多提意見。

    二、annotation實例分析
    1.BRFW(Beaninfo Runtime FrameWork)定義:
    本人編寫的一個annotation功能演示框架。顧名思義,BRFW就是在運行時取得bean信息的框架。

    2.BRFW的功能:
    A.源代碼級annotation:在bean的源代碼中使用annotation定義bean的信息;
    B.運行時獲取bean數(shù)據(jù):在運行時分析bean class中的annotation,并將當前bean class中field信息取出,功能類似xdoclet;
    C.運行時bean數(shù)據(jù)的xml綁定:將獲得的bean數(shù)據(jù)構造為xml文件格式展現(xiàn)。熟悉j2ee的朋友知道,這個功能類似jaxb。

    3.BRFW框架:
    BRFW主要包含以下幾個類:
    A.Persistent類:定義了用于修飾類的固有類型成員變量的annotation。
    B.Exportable類:定義了用于修飾Class的類型的annotation。
    C.ExportToXml類:核心類,用于完成BRFW的主要功能:將具有Exportable Annotation的bean對象轉(zhuǎn)換為xml格式文本。
    D.AddressForTest類:被A和B修飾過的用于測試目的的地址bean類。其中包含了地址定義所必需的信息:國家、省級、城市、街道、門牌等。
    E.AddressListForTest類:被A和B修飾過的友人通訊錄bean類。其中包含了通訊錄所必備的信息:友人姓名、年齡、電話、住址(成員為AddressForTest類型的ArrayList)、備注。需要說明的是電話這個bean成員變量是由字符串類型組成的ArrayList類型。由于朋友的住址可能不唯一,故這里的住址為由AddressForTest類型組成的ArrayList。
    從上面的列表中,可以發(fā)現(xiàn)A、B用于修飾bean類和其類成員;C主要用于取出bean類的數(shù)據(jù)并將其作xml綁定,代碼中使用了E作為測試類;E中可能包含著多個D。
    在了解了這個簡單框架后,我們來看一下BRFW的代碼吧!

    4.BRFW源代碼分析:
    A.Persistent類:
    清單1:

    package com.bjinfotech.practice.annotation.runtimeframework;

    import java.lang.annotation.*;

    /**
    * 用于修飾類的固有類型成員變量的annotation
    * @author cleverpig
    *
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Persistent {
            String value() default "";
    }


    B.Exportable類:
    清單2:

    package com.bjinfotech.practice.annotation.runtimeframework;

    import java.lang.annotation.*;

    /**
    * 用于修飾類的類型的annotation
    * @author cleverpig
    *
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Exportable {
            //名稱
            String name() default "";
            //描述
            String description() default "";
            //省略name和description后,用來保存name值
            String value() default "";
            
    }


    C.AddressForTest類:
    清單3:

    package com.bjinfotech.practice.annotation.runtimeframework;

    /**
    * 用于測試的地址類
    * @author cleverpig
    *
    */
    @Exportable("address")
    public class AddressForTest {
            //國家
            @Persistent
            private String country=null;
            
            //省級
            @Persistent
            private String province=null;
            
            //城市
            @Persistent
            private String city=null;
            
            //街道
            @Persistent
            private String street=null;

            //門牌
            @Persistent
            private String doorplate=null;
            
            public AddressForTest(String country,String province,
                            String city,String street,String doorplate){
                    this.country=country;
                    this.province=province;
                    this.city=city;
                    this.street=street;
                    this.doorplate=doorplate;
            }
            
    }


    D.AddressListForTest類:
    清單4:

    package com.bjinfotech.practice.annotation.runtimeframework;

    import java.util.*;

    /**
    * 友人通訊錄
    * 包含:姓名、年齡、電話、住址(多個)、備注
    * @author cleverpig
    *
    */
    @Exportable(name="addresslist",description="address list")
    public class AddressListForTest {
            //友人姓名
            @Persistent
            private String friendName=null;
            
            //友人年齡
            @Persistent
            private int age=0;
            
            //友人電話
            @Persistent
            private ArrayList<String> telephone=null;
            
            //友人住址:家庭、單位
            @Persistent
            private ArrayList<AddressForTest> AddressForText=null;
            
            //備注
            @Persistent
            private String note=null;
            
            public AddressListForTest(String name,int age,
                            ArrayList<String> telephoneList,
                            ArrayList<AddressForTest> addressList,
                            String note){
                    this.friendName=name;
                    this.age=age;
                    this.telephone=new ArrayList<String>(telephoneList);
                    this.AddressForText=new ArrayList<AddressForTest>(addressList);
                    this.note=note;
                    
            }
    }


    E.ExportToXml類:
    清單5:

    package com.bjinfotech.practice.annotation.runtimeframework;

    import java.lang.reflect.Field;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.ArrayList;

    /**
    * 將具有Exportable Annotation的對象轉(zhuǎn)換為xml格式文本
    * @author cleverpig
    *
    */
    public class ExportToXml {
            /**
             * 返回對象的成員變量的值(字符串類型)
             * @param field 對象的成員變量
             * @param fieldTypeClass 對象的類型
             * @param obj 對象
             * @return 對象的成員變量的值(字符串類型)
             */
            private String getFieldValue(Field field,Class fieldTypeClass,Object obj){
                    String value=null;
                    
                    try{
                            if (fieldTypeClass==String.class){
                                    value=(String)field.get(obj);
                            }
                            else if (fieldTypeClass==int.class){
                                    value=Integer.toString(field.getInt(obj));
                            }
                            else if (fieldTypeClass==long.class){
                                    value=Long.toString(field.getLong(obj));
                            }
                            else if (fieldTypeClass==short.class){
                                    value=Short.toString(field.getShort(obj));
                            }
                            else if (fieldTypeClass==float.class){
                                    value=Float.toString(field.getFloat(obj));
                            }
                            else if (fieldTypeClass==double.class){
                                    value=Double.toString(field.getDouble(obj));
                            }
                            else if (fieldTypeClass==byte.class){
                                    value=Byte.toString(field.getByte(obj));
                            }
                            else if (fieldTypeClass==char.class){
                                    value=Character.toString(field.getChar(obj));
                            }
                            else if (fieldTypeClass==boolean.class){
                                    value=Boolean.toString(field.getBoolean(obj));
                            }
                    }
                    catch(Exception ex){
                            ex.printStackTrace();
                            value=null;
                    }
                    return value;
            }
            
            /**
             * 輸出對象的字段,當對象的字段為Collection或者Map類型時,要調(diào)用exportObject方法繼續(xù)處理
             * @param obj 被處理的對象
             * @throws Exception
             */
            public void exportFields(Object obj) throws Exception{
                    Exportable exportable=obj.getClass().getAnnotation(Exportable.class);        
                    if (exportable!=null){
                            if (exportable.value().length()>0){
    //                                System.out.println("Class annotation Name:"+exportable.value());
                            }
                            else{
    //                                System.out.println("Class annotation Name:"+exportable.name());
                            }
                    }
                    else{
    //                        System.out.println(obj.getClass()+"類不是使用Exportable標注過的");
                    }
                    
                    //取出對象的成員變量
                    Field[] fields=obj.getClass().getDeclaredFields();
                    
                    for(Field field:fields){
                            //獲得成員變量的標注
                            Persistent fieldAnnotation=field.getAnnotation(Persistent.class);
                            if (fieldAnnotation==null){
                                    continue;
                            }
                            //重要:避免java虛擬機檢查對私有成員的訪問權限
                            field.setAccessible(true);
                            Class typeClass=field.getType();
                            String name=field.getName();
                            String value=getFieldValue(field,typeClass,obj);
                            
                            //如果獲得成員變量的值,則輸出
                            if (value!=null){
                                    System.out.println(getIndent()+"<"+name+">\n"
                                                    +getIndent()+"\t"+value+"\n"+getIndent()+"</"+name+">");
                            }
                            //處理成員變量中類型為Collection或Map
                            else if ((field.get(obj) instanceof Collection)||
                                            (field.get(obj) instanceof Map)){
                                    exportObject(field.get(obj));
                            }
                            else{
                                    exportObject(field.get(obj));
                            }
                            
                    }
            }
            
            //縮進深度
            int levelDepth=0;
            //防止循環(huán)引用的檢查者,循環(huán)引用現(xiàn)象如:a包含b,而b又包含a
            Collection<Object> cyclicChecker=new ArrayList<Object>();
            
            /**
             * 返回縮進字符串
             * @return
             */
            private String getIndent(){
                    String s="";
                    for(int i=0;i<levelDepth;i++){
                            s+="\t";
                    }
                    return s;
            }
            /**
             * 輸出對象,如果對象類型為Collection和Map類型,則需要遞歸調(diào)用exportObject進行處理
             * @param obj
             * @throws Exception
             */
            public void exportObject(Object obj) throws Exception{
                    Exportable exportable=null;
                    String elementName=null;
                    
                    //循環(huán)引用現(xiàn)象處理
                    if (cyclicChecker.contains(obj)){
                            return;
                    }
                    
                    cyclicChecker.add(obj);
                    
                    //首先處理Collection和Map類型
                    if (obj instanceof Collection){
                            for(Iterator i=((Collection)obj).iterator();i.hasNext();){
                                    exportObject(i.next());
                            }
                    }
                    else if (obj instanceof Map){
                            for(Iterator i=((Map)obj).keySet().iterator();i.hasNext();){
                                    exportObject(i.next());
                            }
                    }
                    else{

                            exportable=obj.getClass().getAnnotation(Exportable.class);
                            //如果obj已經(jīng)被Exportable Annotation修飾過了(注意annotation是具有繼承性的),
                            //則使用其name作為輸出xml的元素name
                            if (exportable!=null){
                                    if (exportable.value().length()>0){
                                            elementName=exportable.value();
                                    }
                                    else{
                                            elementName=exportable.name();
                                    }
                            }
                            //未被修飾或者Exportable Annotation的值為空字符串,
                            //則使用類名作為輸出xml的元素name
                            if (exportable==null||elementName.length()==0){
                                    elementName=obj.getClass().getSimpleName();
                            }
                            //輸出xml元素頭
                            System.out.println(getIndent()+"<"+elementName+">");
                            levelDepth++;
                            //如果沒有被修飾,則直接輸出其toString()作為元素值
                            if (exportable==null){
                                    System.out.println(getIndent()+obj.toString());
                            }
                            //否則將對象的成員變量導出為xml
                            else{
                                    exportFields(obj);
                            }
                            levelDepth--;
                            //輸出xml元素結尾
                            System.out.println(getIndent()+"</"+elementName+">");
                            
                    }
                    cyclicChecker.remove(obj);
            }
            
            public static void main(String[] argv){
                    try{
                            AddressForTest ad=new AddressForTest("China","Beijing",
                                            "Beijing","winnerStreet","10");
                            
                            ExportToXml test=new ExportToXml();
                            
                            ArrayList<String> telephoneList=new ArrayList<String>();
                            telephoneList.add("66608888");
                            telephoneList.add("66608889");
                            
                            ArrayList<AddressForTest> adList=new ArrayList<AddressForTest>();
                            adList.add(ad);
                            
                            AddressListForTest adl=new AddressListForTest("coolBoy",
                                            18,telephoneList,adList,"some words");
                            
                            test.exportObject(adl);
                    }
                    catch(Exception ex){
                            ex.printStackTrace();
                    }
            }
    }


    在ExportToXml類之前的類比較簡單,這里必須說明一下ExportToXml類:此類的核心函數(shù)是exportObject和exportFields方法,前者輸出對象的xml信息,后者輸出對象成員變量的信息。由于對象類型和成員類型的多樣性,所以采取了以下的邏輯:

    在exportObject方法中,當對象類型為Collection和Map類型時,則需要遞歸調(diào)用exportObject進行處理;
    而如果對象類型不是Collection和Map類型的話,將判斷對象類是否被Exportable annotation修飾過:
    如果沒有被修飾,則直接輸出<對象類名>對象.toString()</對象類名>作為xml綁定結果的一部分;
    如果被修飾過,則需要調(diào)用exportFields方法對對象的成員變量進行xml綁定。

    在exportFields方法中,首先取出對象的所有成員,然后獲得被Persisitent annotation修飾的成員。在其后的一句:field.setAccessible(true)是很重要的,因為bean類定義中的成員訪問修飾都是private,所以為了避免java虛擬機檢查對私有成員的訪問權限,加上這一句是必需的。接著后面的語句便是輸出<成員名>成員值</成員名>這樣的xml結構。像在exportObject方法中一般,仍然需要判斷成員類型是否為Collection和Map類型,如果為上述兩種類型之一,則要在exportFields中再次調(diào)用exportObject來處理這個成員。

    在main方法中,本人編寫了一段演示代碼:建立了一個由單個友人地址類(AddressForTest)組成的ArrayList作為通訊錄類(AddressForTest)的成員的通訊錄對象,并且輸出這個對象的xml綁定,運行結果如下:

    清單6:

    <addresslist>
            <friendName>
                    coolBoy
            </friendName>
            <age>
                    18
            </age>
            <String>
                    66608888
            </String>
            <String>
                    66608889
            </String>
            <address>
                    <country>
                            China
                    </country>
                    <province>
                            Beijing
                    </province>
                    <city>
                            Beijing
                    </city>
                    <street>
                            winnerStreet
                    </street>
                    <doorplate>
                            10
                    </doorplate>
            </address>
            <note>
                    some words
            </note>
    </addresslist>


    三、APT實例分析:
    1.何謂APT?
    根據(jù)sun官方的解釋,APT(annotation processing tool)是一個命令行工具,它對源代碼文件進行檢測找出其中的annotation后,使用annotation processors來處理annotation。而annotation processors使用了一套反射API并具備對JSR175規(guī)范的支持。
    annotation processors處理annotation的基本過程如下:首先,APT運行annotation processors根據(jù)提供的源文件中的annotation生成源代碼文件和其它的文件(文件具體內(nèi)容由annotation processors的編寫者決定),接著APT將生成的源代碼文件和提供的源文件進行編譯生成類文件。
    簡單的和前面所講的annotation實例BRFW相比,APT就像一個在編譯時處理annotation的javac。而且從sun開發(fā)者的blog中看到,java1.6 beta版中已將APT的功能寫入到了javac中,這樣只要執(zhí)行帶有特定參數(shù)的javac就能達到APT的功能。

    2.為何使用APT?
    使用APT主要目的是簡化開發(fā)者的工作量,因為APT可以在編譯程序源代碼的同時,生成一些附屬文件(比如源文件、類文件、程序發(fā)布描述文字等),這些附屬文件的內(nèi)容也都是與源代碼相關的。換句話說,使用APT就是代替了傳統(tǒng)的對代碼信息和附屬文件的維護工作。使用過hibernate或者beehive等軟件的朋友可能深有體會。APT可以在編譯生成代碼類的同時將相關的文件寫好,比如在使用beehive時,在代碼中使用annotation聲明了許多struct要用到的配置信息,而在編譯后,這些信息會被APT以struct配置文件的方式存放。

    3.如何定義processor?
    A.APT工作過程:
    從整個過程來講,首先APT檢測在源代碼文件中哪些annotation存在。然后APT將查找我們編寫的annotation processor factories類,并且要求factories類提供處理源文件中所涉及的annotation的annotation processor。接下來,一個合適的annotation processors將被執(zhí)行,如果在processors生成源代碼文件時,該文件中含有annotation,則APT將重復上面的過程直到?jīng)]有新文件生成。

    B.編寫annotation processors:
    編寫一個annotation processors需要使用java1.5 lib目錄中的tools.jar提供的以下4個包:
    com.sun.mirror.apt: 和APT交互的接口;
    com.sun.mirror.declaration: 用于模式化類成員、類方法、類聲明的接口;
    com.sun.mirror.type: 用于模式化源代碼中類型的接口;
    com.sun.mirror.util: 提供了用于處理類型和聲明的一些工具。

    每個processor實現(xiàn)了在com.sun.mirror.apt包中的AnnotationProcessor接口,這個接口有一個名為“process”的方法,該方法是在APT調(diào)用processor時將被用到的。一個processor可以處理一種或者多種annotation類型。
    一個processor實例被其相應的工廠返回,此工廠為AnnotationProcessorFactory接口的實現(xiàn)。APT將調(diào)用工廠類的getProcessorFor方法來獲得processor。在調(diào)用過程中,APT將提供給工廠類一個AnnotationProcessorEnvironment 類型的processor環(huán)境類對象,在這個環(huán)境對象中,processor將找到其執(zhí)行所需要的每件東西,包括對所操作的程序結構的參考,與APT通訊并合作一同完成新文件的建立和警告/錯誤信息的傳輸。

    提供工廠類有兩個方式:通過APT的“-factory”命令行參數(shù)提供,或者讓工廠類在APT的發(fā)現(xiàn)過程中被自動定位(關于發(fā)現(xiàn)過程詳細介紹請看http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html)。前者對于一個已知的factory來講是一種主動而又簡單的方式;而后者則是需要在jar文件的META-INF/services目錄中提供一個特定的發(fā)現(xiàn)路徑:
    在包含factory類的jar文件中作以下的操作:在META-INF/services目錄中建立一個名為com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8編碼文件,在文件中寫入所有要使用到的factory類全名,每個類為一個單獨行。

    4.一個簡單的APT實例分析:
    A.實例構成:
    Review類:定義Review Annotation;
    ReviewProcessorFactory類:生成ReviewProcessor的工廠類;
    ReviewProcessor類:定義處理Review annotation的Processor;
    ReviewDeclarationVisitor類:定義Review annotation聲明訪問者,ReviewProcessor將要使用之對Class進行訪問。
    runapt.bat:定義了使用自定義的ReviewProcessor對Review類源代碼文件進行處理的APT命令行。

    B.Review類:
    清單7:

    package com.bjinfotech.practice.annotation.apt;

    /**
    * 定義Review Annotation
    * @author cleverpig
    *
    */
    public @interface Review {
            public static enum TypeEnum{EXCELLENT,NICE,NORMAL,BAD};
            TypeEnum type();
            String name() default "Review";
    }


    C.ReviewProcessorFactory類:
    清單8:

    package com.bjinfotech.practice.annotation.apt;

    import java.util.Collection;
    import java.util.Set;
    import java.util.Arrays;
    import com.sun.mirror.apt.*;
    import com.sun.mirror.declaration.AnnotationTypeDeclaration;
    import com.sun.mirror.apt.AnnotationProcessorEnvironment;
    //請注意為了方便,使用了靜態(tài)import
    import static java.util.Collections.unmodifiableCollection;
    import static java.util.Collections.emptySet;

    /**
    * 生成ReviewProcessor的工廠類
    * @author cleverpig
    *
    */
    public class ReviewProcessorFactory implements AnnotationProcessorFactory{
            /**
             * 獲得針對某個(些)類型聲明定義的Processor
             * @param atds 類型聲明集合
             * @param env processor環(huán)境
             */
            public AnnotationProcessor getProcessorFor(
                            Set<AnnotationTypeDeclaration> atds,
                            AnnotationProcessorEnvironment env){
                    return new ReviewProcessor(env);
            }
            /**
             * 定義processor所支持的annotation類型
             * @return processor所支持的annotation類型的集合
             */
            public Collection<String>         supportedAnnotationTypes(){
                    //“*”表示支持所有的annotation類型
                    //當然也可以修改為“foo.bar.*”、“foo.bar.Baz”,來對所支持的類型進行修飾
                return unmodifiableCollection(Arrays.asList("*"));
        }
            
            /**
             * 定義processor支持的選項
             * @return processor支持選項的集合
             */
            public Collection<String>         supportedOptions(){
                    //返回空集合
                return emptySet();
        }
            
            public static void main(String[] argv){
                    System.out.println("ok");
            }
    }


    D.ReviewProcessor類:
    清單9:

    package com.bjinfotech.practice.annotation.apt;

    import com.sun.mirror.apt.AnnotationProcessor;
    import com.sun.mirror.apt.AnnotationProcessorEnvironment;
    import com.sun.mirror.declaration.TypeDeclaration;
    import com.sun.mirror.util.DeclarationVisitors;
    import com.sun.mirror.util.DeclarationVisitor;

    /**
    * 定義Review annotation的Processor
    * @author cleverpig
    *
    */
    public class ReviewProcessor implements AnnotationProcessor{
            //Processor所工作的環(huán)境
            AnnotationProcessorEnvironment env=null;
            
            /**
             * 構造方法
             * @param env 傳入processor環(huán)境
             */
            public ReviewProcessor(AnnotationProcessorEnvironment env){
                    this.env=env;
            }
            
            /**
             * 處理方法:查詢processor環(huán)境中的類型聲明,
             */
            public void process(){
                    //查詢processor環(huán)境中的類型聲明
                    for(TypeDeclaration type:env.getSpecifiedTypeDeclarations()){
                            //返回對類進行掃描、訪問其聲明時使用的DeclarationVisitor,
                            //傳入?yún)?shù):new ReviewDeclarationVisitor(),為掃描開始前進行的對類聲明的處理
                            //        DeclarationVisitors.NO_OP,表示在掃描完成時進行的對類聲明不做任何處理
                            DeclarationVisitor visitor=DeclarationVisitors.getDeclarationScanner(
                                            new ReviewDeclarationVisitor(),DeclarationVisitors.NO_OP);
                            //應用DeclarationVisitor到類型
                            type.accept(visitor);
                    }
            }
    }


    E.ReviewDeclarationVisitor類:
    清單10:

    package com.bjinfotech.practice.annotation.apt;

    import com.sun.mirror.util.*;
    import com.sun.mirror.declaration.*;

    /**
    * 定義Review annotation聲明訪問者
    * @author cleverpig
    *
    */
    public class ReviewDeclarationVisitor extends SimpleDeclarationVisitor{
            /**
             * 定義訪問類聲明的方法:打印類聲明的全名
             * @param cd 類聲明對象
             */
            public void visitClassDeclaration(ClassDeclaration cd){
                    System.out.println("獲取Class聲明:"+cd.getQualifiedName());
            }
            
            public void visitAnnotationTypeDeclaration(AnnotationTypeDeclaration atd){
                    System.out.println("獲取Annotation類型聲明:"+atd.getSimpleName());
            }
            
            public void visitAnnotationTypeElementDeclaration(AnnotationTypeElementDeclaration aed){
                    System.out.println("獲取Annotation類型元素聲明:"+aed.getSimpleName());
            }
    }


    F.runapt.bat文件內(nèi)容如下:
    清單11:

    E:
    rem 項目根目錄
    set PROJECT_ROOT=E:\eclipse3.1RC3\workspace\tigerFeaturePractice
    rem 包目錄路徑
    set PACKAGEPATH=com\bjinfotech\practice\annotation\apt
    rem 運行根路徑
    set RUN_ROOT=%PROJECT_ROOT%\build
    rem 源文件所在目錄路徑
    set SRC_ROOT=%PROJECT_ROOT%\test
    rem 設置Classpath
    set CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%

    cd %SRC_ROOT%\%PACKAGEPATH%
    apt -nocompile -factory com.bjinfotech.practice.annotation.apt.ReviewProcessorFactory  ./*.java



    四、參考資源:
    http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html
    作者的Blog:http://blog.matrix.org.cn/page/cleverpig

    from: http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html

    posted on 2006-01-13 18:39 weidagang2046 閱讀(2913) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 国产精品自在自线免费观看| 91九色老熟女免费资源站| 免费毛片在线视频| 亚洲中文久久精品无码1| 亚洲免费一级视频| 亚洲国语精品自产拍在线观看| 一区二区三区免费精品视频| 国产精品免费视频播放器| 亚洲熟妇无码一区二区三区导航| 亚洲免费黄色网址| 亚洲13又紧又嫩又水多| 免费观看无遮挡www的视频| 亚洲第一成年网站大全亚洲| 久久国产精品一区免费下载| 亚洲欧洲成人精品香蕉网| 在线观看免费播放av片| 亚洲不卡av不卡一区二区| 久久国产精品免费网站| 亚洲精品视频在线| 成人免费一级毛片在线播放视频| 亚洲天堂男人影院| 亚洲av无码专区在线观看素人| 日韩成人毛片高清视频免费看| 亚洲一本大道无码av天堂| 免费av片在线观看网站| 亚洲成人黄色网址| 亚洲AV无码一区二区三区国产| caoporn国产精品免费| 久久精品国产96精品亚洲| 222www免费视频| 黄页网址大全免费观看12网站| 国产亚洲精品线观看动态图| av无码国产在线看免费网站| 亚洲一级毛片免费观看| 亚洲美女高清一区二区三区| 日本成年免费网站| 中国一级特黄高清免费的大片中国一级黄色片 | 久久99国产综合精品免费| 免费精品国产自产拍在线观看 | 久久久久亚洲精品日久生情| 国产猛烈高潮尖叫视频免费|