@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 |