@Retention
애노테이션이란?
- 애노테이션의 메타 정보가 언제 버려질지에 대한 타이밍을 설정한다.
SOURCE
,CLASS
,RUNTIME
중 하나를 선택할 수 있다.
RetentionPolicy.SOURCE
- 소스코드인 구간에만 유지되고 클래스 파일이 되는 컴파일 과정에서 애노테이션 정보는 사라진다.
RetentionPolicy.CLASS
.class
파일에 기록되고, 런타임에 버려진다.- 클래스 파일까지만 유지된다.
- 자바에서의 기본 retention 이다.
질문: RetentionPolicy.CLASS
는 왜 필요한가?
사실 왜 필요한가 싶을 수 있다. '소스코드까지 유지될거면 SOURCE
를 사용하고, 런타임까지 유지될거면 RUNTIME
을 사용하면 되지. 애매하게 CLASS
는 어디다가 쓰라고?'
그러나 라이브러리 배포 관점에서 봤을 때, 자바 라이브러리들은 .class
파일로 관리된다. .java
파일이 아닌 .class
파일로 관리되는 라이브러리에서 메타 정보를 주고 싶다고 가정하자. 만일 SOURCE
로 준다면, 라이브러리가 컴파일되면서 메타정보는 다 날아갈 것이다. 그러면 라이브러리에서 타입체커, IDE 부가기능 등을 사용할 수 없게 된다.
SOURCE 정책으로 사용한다면, 컴파일된 라이브러리의 jar 파일에는 아무것도 남지 않게 될 것이다.
물론, 위의 용도가 아니라도
.class
파일이 되는 시점까지 유지되고 런타임에 버려진다는 특성이 필요하다면 언제든RetentionPolicy.CLASS
를 사용될 수 있다.
RetentionPolicy.RUNTIME
- 런타임 동안에 유지되고, 런타임 동안에 프로그램에서 접근도 가능하다.
- Reflection API 로 접근도 가능하다.
사용 용례 간단히 알아보기
SOURCE
의 사용 예: lombok 의 @Getter
롬복의 @Getter
는 SOURCE
를 사용한다.
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
...
}
소스코드를 컴파일할 때, Getter 메서드를 만들어주면 그 시점에서 @Getter
의 역할은 끝나기 때문이다. 더이상 @Getter
애노테이션 정보를 기억해야 할 이유가 없다.
CLASS
의 사용 예: lombok 의 @NonNull
롬복의 @NonNull
은 CLASS
를 사용한다.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}
@NonNull
에는 각 값에 대해 null 을 체크하는 로직이 있다. 이후에 우리가 @NonNull
을 이용해 작성된 라이브러리를 이용할 때, IDE 는 필수 값에 null 을 넣지 않게 하기 위해 애너테이션을 확인하고 힌트를 줄 것이다. @NonNull
을 CLASS
로 사용하지 않고 SOURCE
로 사용한다면, .class
파일로 작성된 라이브러리들에 대해서 이러한 힌트를 줄 수 없다.
RUNTIME
의 사용 예: 스프링 @Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
// ...
}
스프링에서 대부분의 애노테이션은 RUNTIME
을 사용한다. @Component
이 달려있는 클래스에 대해서는 애플리케이션이 시작된 이후 런타임에 컴포넌트 스캔에 의해 검출되어야 한다.
예제 코드 참고하기
// Java Program to Illustrate Retention Annotations
// Importing required classes from java.lang package
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Here we will be creating 3 annotations with
// RetentionPolicy as SOURCE, CLASS, & RUNTIME
// Retention Annotation 1
@Retention(RetentionPolicy.SOURCE)
// Interface
@interface SourceRetention
{
String value() default "Source Retention";
}
// Retention Annotation 2
@Retention(RetentionPolicy.CLASS)
// Interface
@interface ClassRetention
{
String value() default "Class Retention";
}
// Retention Annotation 3
@Retention(RetentionPolicy.RUNTIME)
// Interface
@interface RuntimeRetention
{
String value() default "Runtime Retention";
}
// Annotating classes A, B, and C
// with our custom annotations
@SourceRetention
class A {
}
@ClassRetention
class B {
}
@RuntimeRetention
class C {
};
// Main class
public class RetentionPolicyDemo {
// Main driver method
public static void main(String[] args)
{
// Obtaining the array of annotations used to
// annotate class A, B, and C. Array a and b will be
// empty as their annotation are attached before
// runtime while array c will contain the
// RuntimeRetention annotation as it was marked with
// RUNTIME retention policy
Annotation a[]
= new A().getClass().getAnnotations();
Annotation b[]
= new B().getClass().getAnnotations();
Annotation c[]
= new C().getClass().getAnnotations();
// Printing the number of retained annotations of
// each class at runtime
System.out.println(
"Number of annotations attached to "
+ "class A at Runtime: " + a.length);
System.out.println(
"Number of annotations attached to "
+ "class B at Runtime: " + b.length);
System.out.println(
"Number of annotations attached to "
+ "class C at Runtime: " + c.length);
// Since the class C is annotated with an annotation
// which which has retention policy as runtime so it
// can be accessed during runtime while annotations
// of other two classes are discarded before runtime
// so they can't be accessed
System.out.println(
"Annotation attached to class C: " + c[0]);
}
}
출력 결과
Number of annotations attached to class A at Runtime: 0
Number of annotations attached to class B at Runtime: 0
Number of annotations attached to class C at Runtime: 1
Annotation attached to class C: @RuntimeRetention(value="Runtime Retention")
코드를 실행시켜보면, 예상대로 자바 코드가 수행된 시간인 런타임에는 @RuntimeRetention
의 애노테이션 정보만 접근 가능했다.
레퍼런스
'Java > 자바 잡지식' 카테고리의 다른 글
스프링 @Bean 애노테이션 정리 (0) | 2022.04.23 |
---|---|
스프링 @Configuration 애노테이션 정리 (0) | 2022.04.23 |
자바 EE 란? (0) | 2022.04.20 |
클린 아키텍처 (by Robert C. Martin) 번역 (0) | 2022.04.14 |
Test Double 이란? (0) | 2022.04.13 |