본문으로 바로가기

1. 어노테이션 (Annotation)

@AnnotationName
  • 어노테이션 = 메타데이터
  • 메타데이터란 애플리케이션이 처리해야 할 데이터가 아니라, 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지 알려주는 정보입니다.
  • 직접 정의해서 사용할 일은 크게 없지만 스프링 프레임워크를 사용한다면 자주 사용하게 되므로 읽을 줄은 알아야 할 것을 권장합니다.

 

2. 어노테이션의 용도

1) 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공(RetentionPolicy.SOURCE)
2) 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공(RetentionPolicy.CLASS)
3) 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공(RetentionPolicy.RUNTIME)

 

Ex) @Override

메소드 선언 시 사용하며, 

메소드가 오버라이드(재정의)된 것임을 컴파일러에게 알려주어 컴파일러가 오버라이드 검사를 하도록 해줍니다.
정확히 오버라이드가 되지 않았다면 컴파일러는 에러를 발생시킵니다.

 

그 외에 다음과 같이 사용합니다. (스프링 프레임워크에서 많이 볼 수 있습니다.)

1) 빌드 시 자동으로 XML 설정 파일을 생성
2) 배포를 위해 JAR 압축 파일을 생성
3) 실행 시 클래스의 역할을 정의

 

3. 어노테이션 타입 정의와 사용

정의

public @interface AnnotationName {

}

 

사용

@AnnotationName

 

element

어노테이션은 element를 멤버로 가질 수 있습니다.

각 엘리먼트는 타입과 이름으로 구성되며, 디폴트 값을 가질 수 있습니다.

엘리먼트 타입으로는 기본 데이터 타입이나 참조 데이터 타입을 사용할 수 있습니다.

public @interface AnnotationName {
    타입 elementName() [default 값];
}

 

예시

어노테이션 정의

public @interface DefaultValues {
    int defaultNumber();
    String defaultString() default "hello annotation";
}

어노테이션 사용

@DefaultValues(defaultNumber = 7) // 기본 값이 있는 element는 생략 할 수 있습니다.
//@DefaultValues(defaultNumber = 7, defaultString = "bye annotation")
public class Main {

}

 

value element

value()

기본 엘리먼트 입니다. 

기본 엘리먼트는 엘리먼트 이름 없이 값만 이용하여 사용할 수 있습니다.

 

예시

어노테이션 정의

public @interface DefaultValues {
    int value();
    String defaultString() default "hello annotation";
}

 

어노테이션 사용

@DefaultValues(7) // value 엘리먼트는 값만 지정할 수 있습니다.
//@DefaultValues(value = 7, defaultString = "bye annotation") // 다른 엘리먼트도 값을 변경하고 싶다면 엘리먼트 이용을 작성해야 합니다.
public class Main {

}

 

4. 어노테이션 적용 대상

@Target 어노테이션과 java.lang.annotation.ElementType 열거 상수를 이용해 어노테이션을 사용할 수 있는 적용 대상을 설정합니다.

public enum ElementType {
    /** 적용 대상: Class, interface, enum */
    TYPE,

    /** 적용 대상: Field */
    FIELD,

    /** 적용 대상: Method */
    METHOD,

    /** 적용 대상: Formal parameter */
    PARAMETER,

    /** 적용 대상: Constructor */
    CONSTRUCTOR,

    /** 적용 대상: Local variable */
    LOCAL_VARIABLE,

    /** 적용 대상: Annotation type */
    ANNOTATION_TYPE,

    /** 적용 대상: Package */
    PACKAGE,

    /**
     * 적용 대상: Type parameter
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE,

    /**
     * Module declaration.
     *
     * @since 9
     */
    MODULE
}

 

예시

1) 하나의 대상을 지정

@Target(ElementType.TYPE) // 클래스, 인터페이스, 열거 타입만 사용가능
public @interface DefaultValues {
    int value();
    String defaultString() default "hello annotation";
}

 

2) 여러 대상을 지정할 경우 {} 안에 지정합니다. (ElementType[])

@Target({ElementType.FIELD, ElementType.CONSTRUCTOR}) // 필드, 생성자만 사용가능
public @interface DefaultValues {
    int value();
    String defaultString() default "hello annotation";
}

이유는 Target 어노테이션이 배열로 값을 받기 때문입니다.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

 

5. 어노테이션 유지 정책

@Retention 어노테이션과 java.lang.annotation.RententionPolicy 열거 상수를 이용해 어노테이션을 어느 범위까지 유지할 것인지 설정합니다.

public enum RetentionPolicy {
    /**
     * 소스상에서만 어노테이션 정보를 유지
     * 소스 코드를 분석할 때만 의미가 있으며, 바이트 코드 파일에는 정보가 남지 않습니다.
     */
    SOURCE,

    /**
     * 바이트 코드 파일까지 어노테이션 정보를 유지
     * 리플렉션을 이용해서 어노테이션 정보를 얻을 수 없습니다.
     */
    CLASS,

    /**
     * 바이트 코드 파일까지 어노테이션 정보를 유지
     * 리플렉션을 이용해서 런타임 시에 어노테이션 정보를 얻을 수 있습니다.
     * 리플렉션: 런타임 시에 클래스의 메타 정보를 얻는 기능
     * Class 객체를 이용해서 클래스의 생성자, 필드, 메소드 정보, 적용된 어노테이션 정보를 알아낼 수 있습니다.
     */
    RUNTIME
}

 

예시

@Retention(RetentionPolicy.RUNTIME) // 런타임 유지
public @interface DefaultValues {
    int value();
    String defaultString() default "hello annotation";
}

 

6. 어노테이션 사용하기(런타임 정책)

  • 어노테이션 자체는 아무런 동작을 가지지 않는 표식입니다. 
  • 리플렉션을 이용해서 어노테이션의 적용 여부와 엘리먼트 값을 읽고 적절히 처리할 수 있습니다.
  • 클래스에 적용된 어노테이션 정보를 얻으려면 java.lang.Class를 이용합니다.
  • 필드, 생성자, 메소드에 적용된 어노테이션 정보를 얻으려면 Class의 메소드를 이용하여 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 합니다.

 

관련 메소드

[클래스 정보 메소드 java.lang.Class ]

Field[] getFields() → 필드 정보 리턴

Constructor[] getConstructors() → 생성자 정보 리턴

Method[] getDeclaredMethods() → 메소드 정보 리턴

 

[어노테이션 정보 메소드 → java.lang.reflect.*]

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

→ 지정한 어노테이션이 적용되었는지 여부(Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true를 리턴)

 

<T extends Annotation> T getAnnotation(Class<T> annotationClass) 

 지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴

(Class에서 호출했을 때 상위 클래스에 적용된 경우에도 어노테이션을 리턴)

 

Annotation[] getAnnotations()

→ 적용된 모든 어노테이션을 리턴(Class에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함)

적용된 어노테이션이 없을 경우 길이가 0인 배열을 리턴

 

Annotation[] getDeclaredAnnotations()  직접 적용된 모든 어노테이션을 리턴

(Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않음)

 

예시

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD) // 필드 적용
@Retention(RetentionPolicy.RUNTIME) // 런타임 유지
public @interface DefaultValues {
    String value() default "veneas";
}
public class Test {
    // 필드에 어노테이션 선언
    @DefaultValues()
    public String name1;

    @DefaultValues("kim")
    public String name2;
}
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) {
        // Test 클래스의 필드 정보 얻기
        Field[] fields = Test.class.getFields();

        for(Field field : fields) {
            // 어노테이션 적용 여부 확인
            if(field.isAnnotationPresent(DefaultValues.class)) {
                // 어노테이션 객체 얻기
                DefaultValues defaultValues = field.getAnnotation(DefaultValues.class);

                // 적용한 어노테이션의 기본 엘리먼트 출력
                System.out.println(defaultValues.value()); // veneas, kim
            }
        }
    }
}