インタビュアー:注釈について話しますか?

Javaアノテーションを理解する

アノテーションはJavaの非常に重要な部分であり、常に使用しています。特にSpringフレームワークを使用して開発する場合は、プロジェクトで多くの奇妙なアノテーション(@ DataScope、@ Log、@ Overrideなど)を使用します。 、多くの場合、これらのアノテーションを使用するだけですが、アノテーションの背後にあるロジックと動作原理についてはよくわかりません。もちろん、自分でアノテーションを開発することもできません。そこで本日は、注釈についての私の理解と、注釈をカスタマイズしてあなたとコミュニケーションをとる方法を記録します。

まず、アノテーションはJava 1.5で導入された概念であり、タイプに属します。アノテーションは、プログラムコード(クラス、メソッド、フィールド)を変更するための一連のデータを提供しますが、アノテーションは変更されたコードの一部ではありません。つまり、コードの操作に直接影響を与えることはなく、コンパイラがどちらを決定します。実行する操作

次に、Annatation(アノテーション)はインターフェースです。リフレクションを介して指定されたプログラムの要素のAnnatationオブジェクトを取得し、オブジェクトを介してアノテーションのメタデータ情報を取得できます。

@Overrideで開始

おそらく、@ Overrideは通常のコーディングで最もよく知られています。それが実際にメソッドの書き換えに使用され、サブクラスが親クラスのメソッドによって使用されるアノテーションをオーバーライドすることを知っています。しかし、どのようにしてこの機能を実現するのでしょうか。ソースコードをクリックして見てみましょう。

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

ソースコードが非常に短いことがわかります。これは、jarパッケージをインポートし、他の2つのアノテーションを使用してから、@ interfaceを使用してこのアノテーションを宣言するだけです。

@Targetと@Retentionをクリックすると、3つのアノテーション@ Documented、@ Retention、@ Targetの間で常にクレイジーであることがわかります。実際、これら3つのアノテーションはアノテーションのメタアノテーションです。

メタアノテーション

これらの3つのメタアノテーションを個別に見てみましょう。

@Retention(ライフサイクル)

マークされた注釈を保存する方法を示します(保存レベルを指定します)。一般的な理解はライフサイクルです。

  • ソース:ソースコードレベル、注釈はコンパイラによって破棄され、コンパイルされたクラスファイルに保持されません
  • CLASS :(デフォルトレベル)クラスファイルレベル。注釈はクラスファイルで使用できますが、JVMによって破棄され、実行中に仮想マシンにロードされません。
  • ランタイム:ランタイムレベル。ランタイム(JVM)中にも予約されるため、リフレクションメカニズムを介してアノテーションのコンテンツを読み取ることができます。SpringMvcの@ Controller、@ Autowired、@ RequestMappingなど。

@目標

マークされたアノテーションがJava要素の種類(クラス、インターフェース、属性、メソッド)に使用できることを示します。全部で8種類あります。

その中で、ElementTypeは列挙型であり、次のように定義されます。可能な値の範囲を示します

public enum ElementType{
     // 标明该注解可以用于类、接口(包括注解类型)或enum声明
    TYPE,

    // 标明该注解可以用于字段(域)声明,包括enum实例 
    FIELD,

    // 标明该注解可以用于方法声明 
    METHOD,

    // 标明该注解可以用于参数声明 
    PARAMETER,

    // 标明注解可以用于构造函数声明 
    CONSTRUCTOR,

    // 标明注解可以用于局部变量声明 
    LOCAL_VARIABLE,

    // 标明注解可以用于注解声明(应用于另一个注解上)
    ANNOTATION_TYPE,

    // 标明注解可以用于包声明 
    PACKAGE,

    //标明注解可以用于类型参数声明(1.8新加入)
    TYPE_PARAMETER,

    // 类型使用声明(1.8新加入)
    TYPE_USE
}

アノテーションが指定されたターゲット値である場合、このアノテーションは任意の要素で使用でき、複数の値が{}に含まれ、コンマで区切られます。

@Documented

指定された注釈を使用する場合は常に、Javadocツールを使用してこれらの要素を記録する必要があります。(つまり、生成されたjavadocにコメントが追加されます)

@遺伝性の

アノテーションタイプは、クラスの宣言にのみ使用されるスーパークラスから継承できます(インターフェイスは継承しません)

@Repeatable

Java SE 8で導入された、マークを表すアノテーションは、同じ宣言またはタイプの使用法に複数回適用できます。

注釈の分類

メタアノテーションの理解を通して、アノテーションがこれらのメタアノテーションによって変更されることを理解し、重要なメッセージも取得しました-アノテーションはアノテーションを変更できます

そのような無限のマトリョーシカにはさまざまな注釈がありますが、どのような注釈がありますか?一般的な注釈は、大きく次の4つのカテゴリに分類されます。

  • メタアノテーション:1つのタイプには5つのタイプがあります
  • jdkアノテーション:一般的なものは@ Override、@ Deprecated:古いメソッドやクラスなどを示すために使用されます。
  • サードパーティのアノテーション:これは、私たちがよく知っているサードパーティのフレームワークSpringなどのアノテーションです。@ Autowiredなどです。
  • カスタムアノテーション:プロジェクトの要件に応じて開発者がカスタマイズしたアノテーションは、コンパイル時と実行時に一部のツールの分析と使用に使用され、説明と構成の役割を果たします。記事の最後に、実際に注釈。

注釈プロセッサ

注釈を使用するプロセスの重要な部分は、注釈プロセッサを作成することです

  • 例:

    • 定義アノテーション:
import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider{
    public int id() default -1;
    public String name() default "";
    public String address() default "";
}
    • 注釈の使用:
public class Apple {
    @FruitProvider(id=1,name="zhonghu",address = "china")
    private String appleProvider;

    public String getAppleProvider() {
        return appleProvider;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
}
    • 注釈プロセッサ:
import java.lang.reflect.Field;


public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){
        String strFruitProvicer = "供应商信息:";
        Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitProvider.class)) {
                FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
                //注解信息的处理地方
                strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:"
                        + fruitProvider.name() + " 供应商地址:"+ fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}
    • 出力
public class FruitRun {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}
  • 結果:
    画像

注釈の役割

  • フォーマットはコンパイル時にチェックされます。といった@Override
  • コードの依存関係を追跡し、構成ファイルを置き換える機能を実現します。注釈情報を処理してコードとXMLファイルを生成します。
  • 一部のコメントは実行時に確認できます

コメントを開始

実際の戦闘を開始する前に、カスタムアノテーションを作成するためのルールも理解する必要があります

ルール

  • アノテーションの定義は@interface、すべてのアノテーションがjava.lang.Annotationこのインターフェースを自動的に継承し、他のクラスまたはインターフェースを継承できなくなることです。

アノテーションは継承をサポートしていません

  • パラメータメンバーは、権限文字publicまたはdefault(默认)アクセス権限文字でのみ変更できます
  • 基本的なデータ・タイプで8つのパラメーター部材、StringEnumClassannotations他のデータ型、および配列のこれらのタイプおよび

パッケージタイプは許可されていません。注釈は要素タイプ、つまりネストされた注釈としても使用できます。

  • クラスのメソッドとフィールドのアノテーション情報を取得するには、Javaリフレクションメカニズムを介して取得する必要があります

同時に、実行時にアノテーションの関連情報を正確に取得するために、javaはjava.lang.reflectのリフレクションパッケージの下にAnnotatedElementインターフェイスを追加しました。これは、現在プログラムでアノテーションが付けられている要素を示すために使用されます。このメソッドは、リフレクションテクノロジーを使用して、注釈付き情報を読み取ることができます。

  • 注釈には、定義されたメンバーを含めることもできません(識別のみ)

注釈を書く

上記では、アノテーションの使用とアノテーションの情報を紹介しました。以下では、アノテーションのフィールドにマークを付けて、JSONにシリアル化するときにオブジェクトにこのフィールドを含める必要があるかどうかをマークしましょう。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
    public String value() default "";
}
  • 説明:

    • JsonFieldアノテーションのライフサイクルはRUNTIMEであり、実行時に有効です。
    • JsonFieldアノテーション変更の目標は、フィールド用のFIELDです。
    • アノテーションを作成するには@interfaceキーワードが必要です
    • JsonFieldアノテーションにはパラメータが1つだけあり、名前はvalue、タイプはString、デフォルト値は空の文字列です。

パラメータ名はvalueであり、注釈のユーザーは名前を指定せずにパラメータを指定できます。つまり、フィールドで@JsonField(value = "塚狐")を使用するか、value =を省略して@JsonField( "塚狐")になることができます。

    • デフォルトの「」では、パラメータの名前と値を指定せずに、フィールドで直接@JsonFieldを使用できます。

注釈を使用する

年齢、名前、住所の3つのフィールドを含むクラスを作成します。フィールドをシリアル化するには、最後の2つが必要です。

public class People {
    private  int age ;

    @JsonField("writeName")
    private String name;

    @JsonField
    private String address;

    public People(int age,String name,String address){
        this.age=age;
        this.name=name;
        this.address=address;
    }

    @Override
    public String toString() {
        return "People{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

その中で:

  • 名前の@JsonFieldアノテーションは、表示される文字列値を提供します
  • アドレスの@JsonFieldアノテーションはデフォルト値を使用します

次に、シリアル化クラスJsonSerializerを記述します。

public class JsonSerializer {
    public static String serialize(Object object) throws IllegalAccessException {
        Class<?> objectClass = object.getClass();
        Map<String, String> jsonElements = new HashMap<>();
        for (Field field : objectClass.getDeclaredFields()) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(JsonField.class)) {
                jsonElements.put(getSerializedKey(field), (String) field.get(object));
            }
        }
        return toJsonString(jsonElements);
    }

    private static String getSerializedKey(Field field) {
        String annotationValue = field.getAnnotation(JsonField.class).value();
        if (annotationValue.isEmpty()) {
            return field.getName();
        } else {
            return annotationValue;
        }
    }

    private static String toJsonString(Map<String, String> jsonMap) {
        String elementsString = jsonMap.entrySet()
                .stream()
                .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
                .collect(Collectors.joining(","));
        return "{" + elementsString + "}";
    }
}

それぞれの意味と機能を見てみましょう。

  • serialize()このメソッドはオブジェクトをシリアル化するために使用され、Object型のパラメーターを受け取ります。objectClass.getDeclaredFields()リフレクションによってオブジェクトによって宣言されたすべてのフィールドを取得してから、forループトラバーサルを実行します。forループでは、によって最初field.setAccessible(true)再びにより、使用の配列のための真に反射物体アクセシビリティセット、(このステップせず、その後、プライベートフィールドは、IllegalAccessExceptionが例外がスローを得ることができない)isAnnotationPresent()フィールド飾らかどうかを決定するJsonFieldアノテーション、もししたがって、getSerializedKey()メソッドを呼び出して、オブジェクトを示す値フィールドを取得し、jsonElementsに配置します。
  • getSerializedKey() このメソッドは、フィールドの注釈の値を取得するために使用されます。注釈の値が空の場合、フィールド名が返されます。
  • toJsonString() このメソッドは、Streamを使用してフォーマットされたJSON文字列を返します。

テストノート

public class JsonFileTest {
    public static void main(String[] args) throws IllegalAccessException{
        People cmower = new People(18,"冢狐","中国");
        System.out.println(JsonSerializer.serialize(cmower));
    }
}

  • 結果:

{"WriteName": "Mound Fox"、 "address": "China"}

  • 分析

    • まず、年齢フィールドは@JsonFieldによってアノテーションが付けられていないため、シリアル化されません
    • 名前は@JsonFieldアノテーションを変更し、文字列writerNameが指定されていることを示しているため、シリアル化後にwriteNameになります
    • アドレスフィールドは@JsonFieldアノテーションで装飾されていますが、指定された値は表示されないため、シリアル化後もアドレスのままです。

やっと

  • 読んでやりがいを感じたら、いいねをあげたいと思います。これが更新の最大のモチベーションになります。ご支援ありがとうございます。
  • Javaとコンピュータの基本的な知識に焦点を当てた私の公開アカウント[JavaFox]に注目してください。私を信じていない場合は、私を叩いてください。
  • 読んだ後に異なる意見や提案がある場合は、コメントして私たちと共有してください。皆様のご支援、ご愛顧を賜りますようお願い申し上げます。

-私は竹湖です。あなたと同じくらいプログラミングが大好きです。

画像

おすすめ

転載: blog.csdn.net/issunmingzhi/article/details/112000502