어노테이션의 용도
어노테이션은 다음 세 가지 용도로 사용된다.
- 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공한다.
- 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
- 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공한다.
어노테이션을 정의하는 방법
어노테이션 기본 정의
public @interface AnnotationName {
}
이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용한다.
@AnnotationName
어노테이션은 엘리먼트(element)를 멤버로 가질 수 있다. 각 엘리먼트는 타입과 이름으로 구성되며, 디폴트 값을 가질 수 있다. 엘리먼트의 타입은 원시 타입, String, 열거 타입, Class 타입, 그리고 이들의 배열 타입을 사용할 수 있다.
public @interface AnnotationName {
int elementName() default 5;
...
}
이렇게 정의한 어노테이션을 코드에 적용할 때에는 다음과 같이 기술한다. 디폴트 값이 없는 엘리먼트는 꼭 값을 기술해야 한다. 그리고 어노테이션은 기본 엘리먼트인 value를 가질 수 있는데, 어노테이션을 적용할 때 엘리먼트의 이름을 생략해서 값을 기술하면 자동으로 value의 값으로 설정된다.
@AnnotationName(elementName = 1)
어노테이션의 적용 대상
어노테이션을 적용할 수 있는 대상은 java.lang.annotation.ElementType 열거 상수로 다음과 같이 정의되어 있다.
ElementType 열거 상수 | 적용 대상 |
TYPE | 클래스, 인터페이스, 열거 타입 |
ANNOTATION_TYPE | 어노테이션 |
FIELD | 필드 |
CONSTRUCTOR | 생성자 |
METHOD | 메소드 |
LOCAL_VARIABLE | 로컬 변수 |
PACKAGE | 패키지 |
어노테이션이 적용될 대상을 지정할 때에는 @Target 어노테이션을 사용한다. 클래스, 필드에 적용하고 싶다면 다음과 같이 기술한다.
@Target(ElementType.TYPE, ElementType.FIELD)
public @interface AnnotationName {
}
어노테이션 유지 정책
어노테이션을 적용할 때 사용 목적에 따라 어노테이션을 어느 범위까지 유지할 것인지 지정해야 한다. 예를들어 소스상에서만 유지할 건지, 컴파일된 클래스까지 유지할 건지, 런타임 시에도 유지할 것인지 정해야 한다. 어노테이션 유지 정책은 java.lang.annotation.RetentionPolicy 열거 상수로 다음과 같이 정의되어 있다.
RetentionPolicy 열거 상수 | 설명 |
SOURCE | 소스상에서만 어노테이션 정보를 유지한다. 소스 코드를 분석할 때만 의미가 있으며, 바이트 코드 파일에는 정보가 남지 않는다. |
CLASS | 바이트 코드 파일까지 어노테이션 정보를 유지한다. 하지만 리플렉션을 이용해서 어노테이션 정보를 얻을 수는 없다. |
RUNTIME | CLASS와 다르게 리플렉션을 이용해서 런타임 시 어노테이션 정보를 얻을 수 있다. |
어노테이션 유지 정책을 설정할 때에는 @Retention 어노테이션을 사용한다. 코드 자동 생성 툴을 개발하지 않는 이상 우리가 작성하는 어노테이션은 대부분 런타임 시점에 사용하기 위한 용도로 만들어진다.
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName {
}
런타임 시 어노테이션 정보 사용하기
런타임 시에 어노테이션이 적용되었는지 확인하고 엘리먼트 값을 이용해서 특정 작업을 수행하는 방법을 알아본다. 어노테이션 자체는 아무런 동작을 가지지 않는 표식일 뿐이지만, 리플렉션을 이용해서 어노테이션의 적용 여부와 엘리먼트 값을 읽고 적절히 처리할 수 있다. 클래스에 적용된 어노테이션 정보를 얻으려면 java.lang.Class를 이용하면 되지만, 필드, 생성자, 메소드에 적용된 어노테이션 정보를 얻으려면 Class의 다음 메소드를 통해서 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 한다.
리턴 타입 | 메소드명(매개 변수) | 설명 |
Field[] | getFields() | 필드 정보를 Field 배열로 리턴 |
Constructor[] | getConstructors() | 생성자 정보를 Constructor 배열로 리턴 |
Method[] | getDeclaredMethods() | 메소드 정보를 Method 배열로 리턴 |
그런 다음, Class, Field, Constructor, Method가 가지고 있는 다음 메소드를 호출해서 적용된 어노테이션 정보를 얻을 수 있다.
리턴 타입 | 메소드명(매개 변수) |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass |
지정한 어노테이션이 적용되었는지 여부를 확인한다. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true를 리턴한다. | |
Annotation | getAnnotation(Class<T> annotationClass) |
지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 어노테이션을 리턴한다. | |
Annotation[] | getAnnotations() |
적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함한다. 적용된 어노테이션이 없는 경우 빈 배열을 리턴한다. | |
Annotation[] | getDeclaredAnnotations() |
직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않는다. |
어노테이션, 리플렉션을 이용한 간단 예제
어노테이션 정의
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
String value() default "-";
int count() default 15;
}
어노테이션 적용
public class Service {
@PrintAnnotation
public void method1() {
System.out.println("실행 내용1");
}
@PrintAnnotation("*")
public void method2() {
System.out.println("실행 내용2");
}
@PrintAnnotation(value = "#", count = 20)
public void method3() {
System.out.println("실행 내용3");
}
}
리플렉션 사용
public class PrintAnnotationTest {
public static void main(String[] args) {
Method[] declaredMethods = Service.class.getDeclaredMethods();
for(Method method : declaredMethods) {
if(method.isAnnotationPresent(PrintAnnotation.class)) {
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
System.out.println("[" + method.getName() + "] ");
for(int i = 0; i < printAnnotation.number(); i++) {
System.out.print(printAnnotation.value());
}
System.out.println();
try {
method.invoke(new Service());
}
catch (Exception e) {}
System.out.println();
}
}
}
}
'JAVA' 카테고리의 다른 글
[기본 시리즈] JAVA 특징 및 JVM에 대하여 (0) | 2022.08.17 |
---|---|
[컬렉션 프레임워크 끝내기] #List (0) | 2022.07.19 |
JAVA 예외 처리 (0) | 2022.07.11 |
프로젝트 JAVA 컨벤션 (0) | 2022.05.08 |
SOLID 원칙 (0) | 2022.03.13 |