[Java] Annotation

Hyeonsu Bang·2021년 11월 5일
0

Java Basic

목록 보기
5/11
post-thumbnail

어노테이션

어노테이션은 메타데이터와 비슷하다. 적용되는 클래스나 메서드, 파라미터 등 자바 내에서 사용되는 다양한 데이터에 추가적인 정보를 제공한다. 이런 특징을 이용해서 특정 어노테이션을 붙인 파라미터를 아규먼트 리졸버로 처리한다든가 활용할 수 있는 방법들이 있다.

용도는 크게 아래와 같이 세 가지이다.

  • 컴파일러에게 코드 문법 에러 체크하도록 정보 제공
  • 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
  • 런타임 시 특정 기능하도록 정보 부여



어노테이션 타입 정의와 적용


public @interface AnnotationName{}

interface 키워드 앞에 @를 붙여 선언할 수 있다.

어노테이션 엘리먼트


어노테이션 내부에 작성되는 멤버를 element라고 한다. 엘리먼트는 String Enum, Class, 배열 타입을 가질 수 있으며, 작성법은 아래와 같다.
public @interface AnnotationName{

	String element1();
	int element2() default 5;
    
}

특이한 점은 메서드를 선언하는 것처럼 선언 후 소괄호를 붙여주어야 한다는 것이다.

또한 엘리먼트 별로 default라는 키워드로 초기값을 설정해줄 수 있다. default 값을 선언해놓으면 해당 엘리먼트를 명시하지 않거나 엘리먼트 표기 후 값을 넣징 않았을 경우 default 값이 적용된다. 하지만 반대로 default를 선언하지 않은 엘리먼트를 사용할 때 값을 적지 않으면 컴파일 에러가 발생한다.


기본적으로 어노테이션을 사용하는 방법은 아래와 같다.


	@AnnotationName
   	(element1 = "value must be assigned", element2=3 // 기재 안 할 경우 5)
   	public void doSomething(){

   	}

또한 value라는 엘리먼트는 이름을 명시하지 않아도 바로 쓸 수도 있다. Spring에서 많이 쓰는 @PathVariable이나 @RequestMapping에 변수명이나 주소를 바로 쓸 수 있는 것도 사실은 value 엘리먼트를 생략할 수 있기 때문이다.


public @interface AnnotationName{

	String value();

}


@AnnotationName("value" // 또는 value = "value"로 적어도 됨)
public void doSomething(){

	//... code here
    
}

어노테이션 적용 대상

@Target 이라는 어노테이션을 통해 특정 어노테이션이 적용될 범위를 지정할 수 있다. @Target 어노테이션에는 ElementType이라는 enum 타입의 엘리먼트가 있어서 이를 통해 명시적으로 적용 범위를 표시할 수 있다. ElementType enum의 필드는 아래와 같다.


public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

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

명시적으로 표현되어 있어서 따로 부연할 부분은 없는 것 같다. 예를 들어 ElmentType.METHOD라고 명시한다면 해당 어노테이션은 method에만 적용된다는 의미이다.

@Target 어노테이션에는 위에서도 언급한 기본 엘리먼트인 value 엘리먼트가 있다. ElementType 배열 타입 형태이다. 따라서 여러 값을 한 번에 지정할 수 있다. 아래는 @Target의 api doc 설명이다.

@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();
}


코드로 표현해보면 아래와 같다.

@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface AnnotationName{
	elements...
}

해석하자면 @Target으로 설정된 @AnnotationName 은 파라미터와 필드에만 적용될 수 있다는 의미이다.



어노테이션 유지 정책 (@Retention)

어노테이션을 어느 범위까지 유지할 것인가에 대해 설정하는 어노테이션이다.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Target과 마찬가지로 RetentionPolicy 열거 타입의 엘리먼트를 기본적으로 가지고 있다. 아래는 api doc이다.

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.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

  • SOURCE: 소스코드 상태에서만 유지. 컴파일 될 때 유지 안됨.
  • CLASS: 바이트코드까지 정보 유지. 설정 안되어 있을 때 기본 동작. Reflection으로 읽어올 수 없음.
  • RUNTIME: 바이트 코드 까지 어노테이션 정보 유지. Reflection으로 읽어올 수 있음.

Reflection은 런타임 시에 클래스 메타 정보 얻는 기능이다. Class 클래스와 java.lang.reflect의 메서드를 통해 특정 클래스나 인터페이스의 메타데이터(필드, 메서드, 생성자 정보 등)를 얻어오는 것을 말한다. 어노테이션의 유지 정책이 RUNTIME인 경우가 많다.


런타임 시 어노테이션 정보 사용하기


어노테이션 자체는 메타데이터를 담고 있는 표식에 불과하지만 리플렉션을 이용해 어노테이션 적용 여부, 엘리먼트 값을 처리할 수 있다. 클래스에 적용된 어노테이션은 java.lang.Class 클래스를 이용하면 되나, 이외 메서드, 필드, 생성자 등은 java.lang.reflect 패키지의 배열 타입을 얻어내야 한다. 해당되는 메서드는 getMethods(), getConstructors() 등이 있다. 해당 내용은 뒤에 소개될 Class 클래스 편에서 좀 더 알아보도록 한다.


예제에 사용된 코드

reference:

  • 신용권, 이것이 자바다
profile
chop chop. mish mash. 재밌게 개발하고 있습니다.

0개의 댓글