Javaのアノテーション実装原理

一、アノテーションの概要

アノテーションはメタデータとも呼ばれ、JDK 1.5から導入された機能で、コードにマークや説明を付けるために使用されます。パッケージ、クラス、インターフェース、フィールド、メソッドパラメータ、ローカル変数などにアノテーションを適用できます。

1.1、アノテーションの種類

1、JDKアノテーションとフレームワークアノテーション:JDK自体が@Resource、@PostConstructなどの多くのアノテーションを提供しています。また、Springの@Autowired、@Serviceなどの一般的なフレームワークも多くのアノテーションを提供しており、これらのアノテーションは使用時にJDKまたはフレームワークによって自動的に認識・解析されます。

2、メタアノテーション:アノテーションを修飾するためのアノテーションで、@Retention(アノテーションの保持段階を示す)、@Target(アノテーションの使用範囲を示す)、@Inherited(アノテーションの継承可能性を示す)、@Documented(Javadocドキュメント生成の有無を示す)などがあります。

3、カスタムアノテーション:ユーザーが独自のニーズに基づいてカスタムアノテーションを定義できます。

1.2、メタアノテーション

@Retention:アノテーションのライフサイクルを定義し、デフォルトはCLASSです。値の範囲は以下の通りです:

SOURCE コンパイル段階で破棄され、通常コンパイル時に使用されます。例えば@Overrideアノテーションなど
CLASS コンパイル段階でバイトコードに書き込まれますが、クラスロード時に破棄されます
RUNTIME 破棄されず、実行時にも使用可能です。そのためリフレクション機構を通じてアノテーション情報を読み取ることができます。通常カスタムアノテーションはRUNTIMEタイプが採用されます

@Target:アノテーションが修飾できる対象を定義し、デフォルトでは任意の対象を修飾できます。値の範囲は以下の通りです:

TYPE クラス、インターフェース、またはenum宣言を記述するために使用されます。例えば@Service、@Componentなど
FIELD 属性を記述するために使用されます。例えば@JSONFieldなど
METHOD メソッドを記述するために使用されます。例えば@Override
PARAMETER メソッドパラメータを記述するために使用されます。例えばMybatisフレームワークの@Paramなど
CONSTRUCTOR コンストラクタを記述するために使用されます
LOCAL_VARIABLE ローカル変数を記述するために使用されます
ANNOTATION_TYPE アノテーションタイプを記述するために使用されます。例えば@Target自体、@Retention、@Documentアノテーションなど
PACKAGE パッケージ名を記述するために使用されます
TYPE_PARAMETER パラメータタイプを記述するために使用されます
TYPE_USE アノテーションが型を使用する任意のステートメントで使用できることを示します

@Inherited:アノテーションがサブクラスに継承されるかどうかを定義します

@Inheritedアノテーションがアノテーションを修飾している場合、そのアノテーションがクラスを修飾していると、そのクラスのサブクラスもアノテーションを継承します

@Documented:アノテーション情報をJavaドキュメントに追加するかどうかを定義します

1.3、アノテーションの構成

アノテーションは通常、アノテーションを修飾するメタアノテーション、アノテーション名、アノテーションメソッドで構成されます。もちろん、アノテーションを単なるマークとして使用し、メソッドがない場合もあります。例えば@Overrideアノテーションにはメソッドがなく、単なるマークとして使用されます。

二、アノテーションの使用

通常、Webサービスがインターフェースを提供する場合、ユーザーがログインした後にのみアクセスできるようにする必要があります。この場合、各インターフェースでユーザーがログインしているかどうかを判断すると、多くの冗長なコードが発生します。そのため、インターフェースメソッドの実行前にユーザー認証のロジックを一層追加する必要があります。フィルターやインターセプターなどの方法で実現できます。この場合、アノテーションと組み合わせて、ログイン認証が必要なメソッドにカスタムアノテーションを追加し、アノテーションが追加されたメソッドはログイン認証が必要となり、アノテーションがないメソッドはログイン認証が不要となります。実装方法は以下の通りです:

カスタムアノテーション@AuthRequired

@Documented
@Target(ElementType.METHOD) /** メソッドを修飾 */
@Retention(RetentionPolicy.RUNTIME)/** ライフサイクルは実行時 */
public @interface AuthRequired {

    /** ログインしていない場合に例外をスローするかどうか */
    boolean throwException() default false;
}

アノテーションを定義した後、直接使用できますが、アノテーションの効果を有効にするには、アノテーションを取得してビジネスロジックを処理するロジックが必要です。この場合、Javaのリフレクション機構が不可欠です。リフレクション機構を通じて、メソッド、クラス、属性に修飾されたアノテーションを取得し、アノテーションが追加されているかどうかを判断する必要があります。

また、Springフレームワークを使用する場合、AOPを構成してカスタムアノテーションと組み合わせて使用できます。以下のケースは@AuthRequiredアノテーションを処理するロジックです:

@Aspect
@Component
public class SecurityAspect {

    <strong>@Around(</strong><strong>"@annotation(com.example.annotation.AuthRequired)")
    </strong>public Object checkAuthentication(ProceedingJoinPoint joinPoint) throws Throwable {
        if (UserContext.getCurrentUser() == null) {
            System.out.println("ユーザーが認証されていません。401を返します: " + joinPoint.getSignature().getName());
            return Response.unauthorized();
        }
        return joinPoint.proceed();
    }
}

三、アノテーションの実装原理

アノテーション自体にはロジックがなく、マークの役割しか果たしません。実装ロジックは完全にアノテーションを処理するロジックに依存します。アノテーションを処理するには、まずアノテーションを見つける必要があります。この場合、Javaのリフレクション機構が不可欠です。主にConstructor、Class、Method、Fieldなどのリフレクション関連クラスのgetAnnotation(Class annotationClass)メソッドを通じて対応するアノテーションを取得します。アノテーションを取得できれば、アノテーションで修飾されていることを意味します。ケースは以下の通りです:

 1 /** 1.クラス上のアノテーションを検索 */
 2         Annotation classAnnotation = targetClass.getAnnotation(AuthRequired.class);
 3         if(classAnnotation != null){
 4             System.out.println("クラスは@AuthRequiredアノテーションで修飾されています");
 5         }
 6 
 7         /** 2.メソッド上のアノテーションを検索 */
 8         Method[] methods = targetClass.getMethods();
 9         for (Method method : methods){
10             if(method.getAnnotation(AuthRequired.class) != null){
11                 System.out.println("メソッド: " + method.getName() + "は@AuthRequiredアノテーションで修飾されています");
12             }
13         }
14 
15         /** 3.フィールド上のアノテーションを検索 */
16         Field[] fields = targetClass.getFields();
17         for (Field field : fields){
18             if(field.getAnnotation(AuthRequired.class) != null){
19                 System.out.println("フィールド: " + field + "は@AuthRequiredアノテーションで修飾されています");
20             }
21         }
22 
23         /** 4.コンストラクタ上のアノテーションを検索 */
24         Constructor constructor = targetClass.getConstructor(String.class);
25         if(constructor.getAnnotation(AuthRequired.class)!=null){
26             System.out.println("コンストラクタは@AuthRequiredアノテーションで修飾されています");
27         }

タグ: Java アノテーション リフレクション メタデータ 実装原理

7月14日 23:29 投稿