어노테이션은 메타데이터와 비슷하다. 적용되는 클래스나 메서드, 파라미터 등 자바 내에서 사용되는 다양한 데이터에 추가적인 정보를 제공한다. 이런 특징을 이용해서 특정 어노테이션을 붙인 파라미터를 아규먼트 리졸버로 처리한다든가 활용할 수 있는 방법들이 있다.
용도는 크게 아래와 같이 세 가지이다.
public @interface AnnotationName{}
interface 키워드 앞에 @를 붙여 선언할 수 있다.
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
은 파라미터와 필드에만 적용될 수 있다는 의미이다.
어노테이션을 어느 범위까지 유지할 것인가에 대해 설정하는 어노테이션이다.
@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
}
Reflection은 런타임 시에 클래스 메타 정보 얻는 기능이다. Class 클래스와 java.lang.reflect의 메서드를 통해 특정 클래스나 인터페이스의 메타데이터(필드, 메서드, 생성자 정보 등)를 얻어오는 것을 말한다. 어노테이션의 유지 정책이 RUNTIME인 경우가 많다.
reference: