Java注釈と注釈プロセッサの作成

7086 ワード

注記はjava言語の重要な特性です.この文書では、注釈の作成方法、注釈プロセッサを使用する場合の作成方法について説明します.
注釈はjava開発においてずっと重要な地位を占めている.Spring Bootが大いに進んでいる今日、注釈はもっと重要で、フレームワークが提供する様々な注釈は私たちに開発をもっと便利にします.注記は、生活のラベル紙のように、クラス、メソッド、プロパティなどに追加の情報を追加したり、クラス、メソッド、プロパティの追加のプロパティを宣言したりすることができます.フレームワークで提供される注釈に加えて、カスタム注釈を作成することもできます.
注釈の作成方法
まず、springが提供するRestController注釈を例に、注釈がどの部分から構成されているかを見てみましょう.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

    @AliasFor(annotation = Controller.class)
    String value() default "";

}

注釈の宣言
Javaでは、注釈の宣言はクラスの宣言と類似しており、クラスのclassキーワードの代わりに@interfaceキーワードを使用する以外は、インタフェースの宣言(interface、@なし)と混同しないように注意してください.
注記のプロパティ
注記には複数のプロパティを含めることができます.クラスのプロパティとは異なり、注記のプロパティには、プロパティ名の後にカッコを付ける必要があります.これは、パラメータなしのメソッド宣言に似ています.実際には、注釈プロセッサで注釈のプロパティを呼び出す場合も、パラメータなしメソッドを呼び出すのと同じです.この点については、後述します.
注記のプロパティはdefaultキーワードを使用してデフォルト値を割り当てることができます.プロパティにデフォルト値がない場合は、注記を使用するときにこのプロパティに値を割り当てる必要があります.
注記のメタ情報
注記はクラス、メソッド、またはプロパティに使用しますか?注記はソースファイルで有効ですか、classファイルで有効ですか、それとも実行時に呼び出すことができますか.これらはすべて注釈のメタ情報に属します.注釈は、一連のメタ注釈(注釈に表記された注釈)によってこれらのメタ情報を説明する.一般的なメタ注記には、@Target、@Retention、@Documented、@Inherited、および@Repeatable(jdk 8以降に追加)が含まれます.
@Targetは、ElementType列挙タイプの配列を指定することによって、注釈の作用範囲を示します.一般的な範囲には、TYPE(クラスと注記)、FIELD(プロパティ)、METHOD(メソッド)、PARAMETER(メソッドのパラメータ)、CONTRUCTOR(コンストラクタ)、LOCAL_があります.VARABLE(ローカル変数)など.
@Retentionは、注釈の保存ポリシーを示すRetentionPolicyタイプの列挙値を指定します.SOURCE(ソースコード)、CLASS(classファイル)、RUNTIME(ランタイム)の3つの列挙値があります.SOURCEは,注釈がソースコードにのみ保持され,コンパイル時にコンパイラによって破棄されることを示している.CLASSは、注釈をclassファイルにコンパイルできますが、jvmにロードされません.RUNTIMEは、実行時に使用するために注釈をjvmにロードできることを示している.
@Documentedは、この注釈(@Documentedで表記された注釈を指す)がjavadocドキュメントに入ることを示します.すなわち、その注釈で表記されたクラス、メソッド、属性などのjavadocにこの注釈が表示される場合です.
@Inheritedは、この注釈(@Inheritedで表記された注釈を指す)が表記されているクラス、メソッド、属性がそのサブクラスに渡されることを示します.ただし、この注記はサブクラスのjavadocドキュメントには表示されません.
@Repeatableは、現在の注記を格納できるコンテナクラス(たとえば、@Repeatableで表記された注記を指す)を繰り返し使用できることを示します.
@Repeatable(RestControllers.class)

// RestControllers    @RestController     
public @interface RestControllers{
         RestController[] value();
}

通常、注記に@Target、@Retention、@Documentedメタ注記を追加する必要があります.
注釈の使用方法
@Retentionメタ注記で定義された注記の範囲内で注記を使用できます.たとえば、注記が@Retention(RetentionType.TYPE)で表記されている場合、この注記はクラスで使用できます.ここでは、@RestController注釈を例に、注釈の使用方法を説明します.
@RestController(value = "userController")
public class UserController{}

注記された属性は、属性名=属性値で割り当てることができます.デフォルト値のない属性は値を割り当てる必要があります.valueは、valueという名前の注釈属性に値を付け、他の属性に値を付ける必要がない場合は省略できます.すなわち、上記のコードは、以下のように簡略化することができる.
@RestController("userController")
public class UserController{}

配列タイプの属性に値を割り当て、値を単一要素の配列にする場合は、配列のカッコを省略できます.たとえば、次のようにします.
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
        String[] value();
}

//      
@Test(value={"abc"})
public class MyTest{}

//               
@Test("abc")
public class MyTest{}

注釈プロセッサの作成方法
注釈自体の役割は限られており,通常は独立した処理コードと組み合わせて使用する必要がある.注記の処理コードは、個別のクラスに存在してもよいし、他のクラスの論理コードに存在してもよい.注記プロセッサは通常javaの反射機構によって実現される.ここでは、エンティティークラスオブジェクトのセットをEXCELとしてエクスポートする例を示します.
まず@Excel注記を作成します.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
      //    EXCEL     
    String value() default "";
      //       ,         "0- ,1- "
    String map() default "";

}

Personというエンティティクラスがあるとします.
public class Person {
    @Excel("id")
    private Integer id;
    /**
     *   
     */
    @Excel("  ")
    private String name;
    /**
     *   
     */
    @Excel("  ")
    private Integer age;
    /**
     *   
     */
    @Excel(value="  ", map="0- ,1- ")
    private Integer gender;
}

各属性の中国語名を@Excel注記で表記し、性別属性についても対応するマッピング値を示します.Excel出力をシミュレートし、以下の形式でオブジェクトを出力します.
id            
1         25       
2         30       

次に、処理クラスを作成します.
public class ExcelHandler {

    //        
    Map> cache = new HashMap<>();

    public  void handle(List list, Class clazz) throws IllegalAccessException {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //     ,         ,       
            if (field.isAnnotationPresent(Excel.class)) {
                //       
                Excel annotation = field.getAnnotation(Excel.class);
                //       value  ,     
                System.out.print(annotation.value() + " ");
                //      map  , map         HashMap,     ,          
                if (!"".equals(annotation.map())){
                    cache.put(field.getName(), convertToMap(annotation.map()));
                }
            }
        }
        System.out.println();
        for (T t : list) {
            for (Field field : fields) {
                //              
                field.setAccessible(true);
                Object value = field.get(t);
                if (field.isAnnotationPresent(Excel.class)) {
                    Excel annotation = field.getAnnotation(Excel.class);
                    String map = annotation.map();
                    if ("".equals(map)) {
                        System.out.print(value);
                    } else {
                        System.out.print(cache.get(field.getName()).get(String.valueOf(value)));
                    }
                } else {
                    System.out.print(value);
                }
                System.out.print(" ");
            }
            System.out.println();
        }
    }

    /**
     *         HashMap
     * @param fieldMap         , "0- ,1- "
     * @return    HashMap
     */
    private Map convertToMap(String fieldMap) {
        Map map = new HashMap<>();
        String[] arr = fieldMap.split(",");
        for (String s : arr) {
            String[] arr1 = s.split("-");
            map.put(arr1[0], arr1[1]);
        }
        return map;
    }

    public static void main(String[] args) throws IllegalAccessException {
        Person zhangsan = new Person();
        zhangsan.setGender(1);
        zhangsan.setId(1);
        zhangsan.setAge(25);
        zhangsan.setName("  ");

        Person lisi = new Person();
        lisi.setGender(1);
        lisi.setId(1);
        lisi.setAge(25);
        lisi.setName("  ");

        List personList = new ArrayList<>();
        personList.add(zhangsan);
        personList.add(lisi);

        new ExcelHandler().handle(personList, Person.class);

    }
}

ここでの主な考え方は,反射機構によりclassオブジェクトに基づいてクラスの属性を動的に取得し,属性によって属性上の注釈とその属性を取得し,取得した注釈と属性に対して対応する処理を行うことである.