0. 왜 이걸 파보기 시작했는가
Annotation을 잘 사용하고 있고 springboot나 lombok을 사용하는 경우 정말 빼놓을 수가 없습니다. 근데 이게 어떻게 동작하는지 갑자기 의문이 생겼습니다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(String name) {
return "hello " + name;
}
}
@Override는 그렇다 치고 스프링부트 다루다 보면 정말 다양한 어노테이션들을 보게 되는데 그거 진짜 어떻게 동작하는 걸까요
1. 아무튼 그래서 Annotation이란?
https://docs.oracle.com/javase/tutorial/java/annotations/
공식문서 기준으로 좀 봤습니다.
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
메타데이터랍니다. 프로그램에 대한 데이터를 준답니다. 코드의 동작에 직접적으로는 영향을 안 준답니다.
Annotations have a number of uses, among them:
- Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
- Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
- Runtime processing — Some annotations are available to be examined at runtime.
3가지 용도가 있다고 합니다.
- 컴파일러에 대한 정보 제공
- 컴파일 시간 및 배포 시간 처리
- 런타임 처리
우리가 사용하는 어노테이션은 정말 최소한 2개의 요소를 가져야 하며 기본적으로 3개의 구성 요소를 가집니다.
@Retention :: 필수
@Documented :: 옵션인데 거의 필수라고 생각(javadoc 안 만들 건 아니죠?)
@Target :: 필수
1-1. @Retention
@Retention : 해당 어노테이션의 동작 범위를 지정합니다. 파라미터로 RetentionPolicy를 받습니다.
RetentionPolicy enum코드는 아래와 같습니다.
package java.lang.annotation;
/**
* Annotation retention policy. The constants of this enumerated class
* describe the various policies for retaining annotations. They are used
* in conjunction with the{@linkRetention}meta-annotation interface to
* specify how long annotations are to be retained.
*
*@authorJoshua Bloch
*@since1.5
*/
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.
*
*@seejava.lang.reflect.AnnotatedElement
*/
RUNTIME
}
SOURCE, CLASS, RUNTIME이 있습니다.
(실은 설명도 저기에 잘 적혀있긴 합니다.)
- SOURCE : 컴파일 시간에 동작은 안 하고 소스에서 표시만 됩니다.
- CLASS : 컴파일 시간에 동작은 하는데 실제 런타임에는 동작하지 않습니다.
- RUNTIME : 우리가 가장 많이 사용합니다. 해당 Annotation이 적용된 클래스, 메서드가 런타임에서도 동작합니다.
1-2 : @Documented
@Documented : javadoc으로 annotation에 대한 설명을 뺄 수 있게 도와준답니다.
* Concretely, if an annotation interface is annotated with{@codeDocumented},
* by default a tool like javadoc will display annotations of that interface
* in its output while annotations of annotation interfaces without
*{@codeDocumented}will not be displayed.
1-3 : @Target
@Target : 어느 부분에 적용해야 할지 정의합니다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation interface
* can be applied to.
*@returnan array of the kinds of elements an annotation interface
* can be applied to
*/
ElementType[] value();
}
ElementType을 Array로 받을 수 있게 되어있습니다. 즉 하나의 Annotation으로 여러 곳에 사용할 수 있다는 겁니다.
public enum ElementType {
/** Class, interface (including annotation interface), enum, or record
* declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation interface declaration (Formerly known as an annotation type.) */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
*@since1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
*@since1.8
*/
TYPE_USE,
/**
* Module declaration.
*
*@since9
*/
MODULE,
/**
* Record component
*
*@jls8.10.3 Record Members
*@jls9.7.4 Where Annotations May Appear
*
*@since16
*/
RECORD_COMPONENT;
}
실제 ElementType enum 코드를 보면 이렇게 정의되어 있습니다.
- ElementType.ANNOTATION_TYPE
- Annotation에 적용할 수 있습니다.
- ElementType.CONSTRUCTOR
- 생성자에 적용할 수 있습니다.
- ElementType.FIELD
- 필드 또는 속성에 적용할 수 있습니다.
- ElementType.LOCAL_VARIABLE
- 지역 변수에 적용할 수 있습니다.
- ElementType.METHOD
- 메서드 수준 주석에 적용할 수 있습니다.
- ElementType.PACKAGE
- 패키지 선언에 적용할 수 있습니다.
- ElementType.PARAMETER
- 메서드의 매개 변수에 적용할 수 있습니다.
- ElementType.TYPE
- 클래스의 모든 요소에 적용할 수 있습니다.
1-4 : @Inherited
상속에 대한 정의를 할 수 있는 Annotation이 있습니다. 이건 여기서는 크게 다루지 않도록 하겠습니다. 다음에 기회가 된다면 Annotation을 직접 정의하는 걸 올려보겠습니다.
2. 이제 정말 Annotation의 동작 원리를 작성해 보면
아무튼 그래서 이제 진짜 Annotation이 붙은 놈들이 어떻게 동작하는지 적어보겠습니다.
결론은 Annotation Processor라는 놈이 해당 Annotation과 Annotation이 붙은 놈들을 수집해서 byte code를 만들고. class 파일을 만듭니다. 이 과정에서 Parser는 수집(실행)되지 않은 Annotation Processor가 있는지 확인한 후 모두 수행할 때까지 해당 작업을 반복합니다.
2-1. 왜 반복하는지
Oracle은 Processing의 반복 횟수를 Round라고 하기로 했습니다. 한 Round에서는 하나의 Annotaion을 통해 Boilerplate code를 만들고 나서 다시 Parser가 검사를 하는데 이때 Annotation이 중첩이 되어있는 경우 아직 동작하지 않은(Boilerplate code를 만들지 않은) Annotation이 있을 수 있기 때문입니다.
대충 이렇게 굴러갑니다. 다들 잘 알다시피. class파일을 기반으로 jvm의 클래스 로더에 올라가는데 그러면 당연히 그전에 annotation이 붙은 코드들이 잘 바뀌어야겠죠.
3. 결론
- Annotation processor가 동작하기 전에는 그냥 주석과 다를 게 없다.
- 잘 사용하면 Boilerplate code를 정말 멋지고 쉽고 이쁘게 만들 수 있다.
- 편한 만큼 내부는 어려울 수 있다.
Ciation
https://www.adrianbartnik.de/blog/annotation-processing/
https://docs.oracle.com/javase/tutorial/java/annotations/
'JAVA' 카테고리의 다른 글
자네 혹시 finalize를 오버라이딩 했는가? (0) | 2023.08.19 |
---|---|
LinkedList와 ArrayList의 remove 연산 (0) | 2023.06.21 |
f-lab 백엔드 면접 질문 답해보기(아직 만드는 중) (2) | 2022.12.01 |
내 자바 코드 스타일 바꿔보기, 근데 함수형을 곁들인 - filter (0) | 2022.05.07 |
자바 조금 더 잘 사용해보자 (1) (0) | 2022.03.03 |