Java

Java Custom Annotation (자바 커스텀 어노테이션 만들기)

소밍소밍 2021. 7. 9. 10:03

자바에서 커스텀 어노테이션은 @interface로 선언하면 된다. 선언 시 필요한 범위에 따라 @Target, @Retention을 붙여줘야 한다. 아래와 같이 설정하면 실제로 많이 사용하는, 메소드에 붙여 동작을 제어할 수 있는 커스텀 어노테이션이 만들어진다.

 

// @Target({ElementType.METHOD, ElementType.TYPE}) // 여러 타입을 넣을 경우
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
    String value();
}

이제 커스텀 어노테이션을 이루는 구성요소들에 대해 알아보자.

 


@Target - 어노테이션을 적용할 위치를 지정하는 어노테이션

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

위 코드는 주석을 제외한 1.8 기준의 코드인데, Target이 어노테이션 타입으로만 사용되는 어노테이션이기 때문에 ElementType.ANNOTATION_TYPE을 value로 가지고 있는 것을 확인할 수 있다. 만약 @Target을 메소드에 붙이면 '@Target' not applicable to method 이라는 경고 메시지를 확인할 수 있다. 클래스 등 다른 위치에 붙여도 마찬가지로 에러가 발생한다.

 

@Target은 java.lang.annotation.ElementType 타입의 enum 값을 리스트로 받는데, default가 없기 때문에 1개 이상의 ElementType을 지정해야 한다. ElementType의 종류는 아래와 같다.

  • TYPE: Class, interface (annotation type 포함), enum에 선언
  • FIELD: 필드 (enum의 constants도 포함)
  • METHOD: 메소드
  • PARAMETER: 파라미터
  • CONSTRUCTOR: 생성자
  • LOCAL_VARIABLE: 지역 변수
  • ANNOTATION_TYPE: 어노테이션 타입
  • PACKAGE: 패키지

아래 두 가지는 1.8에 추가된 항목이다. (참고링크)

  • TYPE_PARAMETER: 타입 변수에 사용 가능
    • generic class: MyClass<T> 의 T에 사용 가능 - MyClass<@CustomAnnotation T>
    • generic method: <T> foo (...) {...} 의 T에 사용 가능 - <@CustomAnnotation T> foo (...) {...}
    • wildcards: List<?>의 ?에 사용 가능 - List<@CustomAnnotation ?>
  • TYPE_USE: 모든 종류의 타입에 사용 가능 (변수 선언시, generics, casts)
    • @Target(ElementType.TYPE), @Target(ElementType.TYPE_PARAMETER) 두개를 대체할 수 있다. 즉, Class, interface, enum, type parameter 모두에 사용될 수 있다.
    • 단, 타입에만 사용되기 때문에 아래와 같은 경우를 주의해야 한다.
@Target({TYPE_USE}) 
@interface TypeUseAnnotation { 
}

@TypeUseAnnotation java.lang.Object illegalField; // illegal - field에 사용했기 때문
java.lang.@TypeUseAnnotation Object legalField;   // legal - type에 사용했기 때문

@TypeUseAnnotation java.lang.Object illegalMethod () { ... } // illegal - method에 사용했기 때문
java.lang. @TypeUseAnnotation Object legalMethod () { ... }  // legal - type에 사용했기 때문

 

 

@Retention - 자바 컴파일러가 어노테이션을 다루는 방법

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

어떤 시점까지 영향을 미치는지 결정하는 어노테이션으로, java.lang.annotation.RetentionPolicy 타입의 값을 받는다. RetentionPolicy의 종류는 아래와 같다.

  • SOURCE: 컴파일 전까지만 유효하고 컴파일러에 의해 무시됨 - class 파일에 명시되지 않음
  • CLASS: 컴파일까지는 유지되지만 실질적으로 런타임에는 사라짐 (디폴트 설정) - class 파일에 남아 있지만 runtime에는 버려짐
  • RUNTIME: 런타임까지 사용할 수 있음

 

 

어노테이션의 body

 

body는 비워둬도 되고, 필요에 따라 필드, enum, list 등 다양한 값으로 구성할 수 있다. 이 부분은 필요에 따라 설정하면 된다.

단, 어노테이션의 내용으로 1가지만 정의하는 경우 관례적으로 명칭을 value()라고 사용하기 때문에 어노테이션의 내용에 value를 정의한 경우 prefix를 지정하지 않아도 자동으로 value로 처리가 된다. 

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnlyValueAnnotation {
    String value();
}

@OnlyValueAnnotation("myannotation")
public void myMethod () {}

만약 value를 포함하여 다른 내용이 있을 경우 각각 key = value 로 값을 명시해주면 된다.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueAndDataAnnotation {
    String value();
    String data();
}

@ValueAndDataAnnotation("myAnnotation") // illegal - 'data' missing though required
public void myMethod () {}

@ValueAndDataAnnotation(value = "myAnnotation", data = "myData") // legal
public void myMethod2 () {}

value 외의 값에 디폴트 값을 지정해 두면 value 값만 넣어 사용할 수도 있다.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultDataAnnotation {
    String value();
    String data() default "defaultData";
}

@DefaultDataAnnotation("myAnnotation") // legal - data에 디폴트 값이 있기 때문
public void myMethod () {}

@DefaultDataAnnotation(value = "lolo", data = "newData") // legal - data에 다른 값을 넣을수도 있음
public void myMethod2 () {}