Retention

Retention

이 문서에서는 Retention 설정 옵션에 따른 차이를 다룬다.

@interface Retention

Library에서 Retention 코드를 찾아보면 다음과 같다. 편의상 주석은 제거하였다.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
  • @Documented - 작성된 Annotation이 Javadoc에 문서화 됨을 표시

  • @Target - 이 Annotation이 다른 Annotation type에 작성될 수 있음을 표시

Annotation 내부에는 RetentionPolicy라는 Enum 값을 저장할 수 있는 value 속성이 존재한다.

enum RetentionPolicy

RetentionPolicy의 코드는 다음과 같다.

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

설정 가능한 RetentionPolicy 값은 다음과 같다.

  • SOURCE - 컴파일러에 의해 삭제되는 Annotation

  • CLASS - 컴파일러에 의해 클래스파일에 기록되지만 런타임 시 VM에서는 관리하지 않음

  • RUNTIME - 컴파일러에 의해 클래스파일에 기록되고 VM에서 관리하여 실행 중 읽을 수 있음

RetentionPolicy.SOURCE

RetentionPolicy.SOURCE로 설정된 Annotation은 컴파일과 동시에 사라진다. 따라서 다음 용도로 사용이 가능하다.

  1. 코드상에 단순한 표식(Marking) 생성

  2. 컴파일 시 특정 코드로 치환되도록 자동화 구현

코드상에 단순한 표식 설정

예를 들어 테스트가 진행중인 메소드에 다음과 같이 표시를 남겨 정보를 줄 수 있다.

먼저 Annotation을 다음과 같이 생성한다.

그리고 특정 메소드에 Annotation을 작성하고 옵션으로 정보를 설정한다.

생성된 class 파일을 찾아서 디컴파일하면 다음과 같이 표시된다.

@TestInProgress Annotation이 사라진 것을 확인할 수 있다.

컴파일 시 특정 코드로 치환되도록 자동화 구현

대표적인 라이브러리로 Lombok이 있다. Lombok에서 사용하는 @Getter @Setter @ToString @EqualsAndHashCode 등이 해당된다.

lombok 공식 홈페이지에 있는 소개 비디오 영상

Lombok을 이용하여 다음과 같이 클래스를 하나 만든다.

이 클래스에는 Lombok에서 제공하는 Annotation 4종류를 사용하였다.

  • @Data - Getter/Setter, ToString, EqualsAndHashCode 생성

  • @NoArgsConstructor - 기본 생성자 생성

  • @AllArgsConstructor - 모든 필드 초기화 생성자 생성

  • @Builder - 빌더 패턴을 위한 빌더 클래스 생성

디컴파일러를 이용하여 생성된 클래스를 살펴보면 다음과 같다.

생성된 내부에 Java에서 작성한 적이 없는 코드들이 추가되어 있는 것을 확인할 수 있다. 또한 @Builder 로 인하여 빌더 클래스가 하나 더 생긴것을 확인할 수 있다.

사용한 Lombok의 Annotation은 제거된 것을 확인할 수 있다. 각각의 Annotation을 보면 Retention 설정이 RetentionPolicy.SOURCE로 되어 있는 것을 확인할 수 있다.

해당 내용들을 직접 적용하려면 annotation processor 등의 처리가 필요하다.

RetentionPolicy.CLASS

RetentionPolicy.CLASS로 설정된 Annotation은 컴파일 이후의 바이트 코드까지 유지되지만 런타임 시점까지 반드시 유지할 필요는 없다. 따라서 RetentionPolicy.SOURCE와 RetentionPolicy.RUNTIME의 중간 정도로 이해할 수 있다. 보기에 따라서 상당히 애매해 보일 수 있지만 자바 애플리케이션을 만들 때 다수의 jar 파일을 사용하고, 이 jar 파일에는 class 파일만 들어있다는 것을 생각해보면 배포된 파일에는 포함되어야 하지만 실행 시에는 포함될 필요가 없는 경우 사용하는 형태라고 볼 수 있다.

RetentionPolicy.RUNTIME

RetentionPolicy.RUNTIME으로 설정된 Annotation은 VM에서 런타임 시점까지 유지한다. 런타임 시점까지 유지된다는 것은 코드를 통해 Annotation의 유무와 내부에 설정된 옵션을 읽을 수 있다는 의미이다. Java Reflection과 같은 기술을 사용하여 Annotation을 해석하고, 이에 따라 다른 작업을 수행할 수 있다.

다음 Book 클래스를 통해 좀 더 자세히 살펴본다.

Book 클래스에는 @TestEntity 라는 Annotation이 설정되어 있다. @TestEntity의 코드는 다음과 같다.

Retention 설정이 RetentionPolicy.RUNTIME으로 되어 있기 때문에 실행 중에도 Annotation이 유지되며 Java Reflection을 이용하여 이를 알아낼 수 있다.

메소드는 Method, 생성자는 Constructor, 필드는 Field 클래스에 존재하는 annotation 반환 명령을 이용하여 특정 Annotation이 존재하는지 확인할 수 있고, 필요하다면 모든 Annotation 목록을 반환하도록 만들 수 있다.

  • getAnnotation(Class) - 상속된 Annotation을 포함하여 특정 Annotation의 유무 조회

  • getDeclaredAnnotation(Class) - 상속된 Annotation을 제외하고 특정 Annotation의 유무 조회

  • getAnnotations() - 상속된 Annotation을 포함하여 목록 조회

  • getDeclaredAnnotations() - 상속된 Annotation을 제외하고 목록 조회

Last updated