Annotaion
애노테이션
-
Java Doc
자바를 개발한 사람들은 소스코드와 문서를 하나의 파일로 관리하기위해, 소스코드의 주석으로 코드에 대한 정보를 저장하고, 그 주석으로부터 HTML 문서를 생성해 내는 프로그램 (javadoc.exe)를 만들어 사용했다. Javadoc을 사용하여 문서화할 클래스, 필드, 메서드, 어노테이터, 인터페이스 등에 주석 및 어노테이션으로 문서를 작성하고 HTML등 다양한 포맷으로 Export 할 수 있다.
장점
- 표준에 맞춘 주석 작성으로, 구독자 간의 혼동 최소화
- 코드에 문서를 포함시킬 수 있어 개발과 동시에 쉬운 문서화 가능
- 다양한 지원 도구로 원하는 디자인/타입 문서를 쉽게 만들 수 있음
어노테이션이란?
어노테이션(Annotation)은 Java5부터 등장한 기능으로 사전적 정의는 주석 이지만 실제 우리가 사용하는 주석과는 차이가 있다. @
를 이용한 주석으로, 자바코드에 주석을 달아 특별한 의미를 부여한 것이다.
어노테이션으로 인해 데이터의 유효성 검사를 쉽게 할 수 있고, 이와 관련한 코드가 간결해진다. 또한, 어노테이션의 본질적인 목적은 소스코드에 메타데이터를 표현하는 것이다.
- 유효성 검사: 데이터가 요구하는 형식에 맞는지 검사하는 것
- ex. 이메일 형식인지 검사하는 어노테이션
public class Demo { @Email(message = "이메일 형식이 아닙니다.") private String email; }
- 메타데이터 : 데이터를 위한 데이터 즉, 데이터에 대한 설명을 포함한, 자신의 정보를 담고 있는 데이터
- ex.
@Override
: 상속받은 메소드를 동일한 이름으로 새로 작성하는 것
public class Parent { public void method() { System.out.println("Parent 클래스의 method"); } } public class Child extends Parent{ // 에러 발생 @Override public void method(String test) { super.method(); } }
어노테이션은 컴파일러에서 읽을 수 있는 주석이기때문에 오버라이딩에 맞지 않을 때 오류가 나는것을 알려준다.
- ex.
💡 - 컴파일러에게 코드 문법 에러를 체크하도록 정보 제공
- 런타임에 특정 기능을 실행하도록 정보 제공
어노테이션 정의
어노테이션 타입을 정의하는 방법은 인터페이스를 정의하는 것과 유사하다.
public @interface AnnotationName {
// ...
}
사용시에는 @AnnotationName
으로 사용한다.
멤버를 갖는 어노테이션
public @interface MyAnnotation {
String myElement1();
int myElement2() default 5;
public enum WEEK {MONTHDAY, FRIDAY, SUNDAY} // enum 타입 사용 가능
String value();
int[] values();
WEEK week() default WEEK.FRIDAY; // enum 사용 방법
}
사용할 수 있는 엘리먼트 타입으로는 int, double 과같은 primitive type, String, Class 타입 그리고, 이들의 배열 타입을 사용할 수있다.
위 코드에서 정의한 어노테이션을 코드에 적용할 때는 이렇게 사용한다.
@Annotation(myElement1="TEST", myElement2=10);
@MyAnnotation(myElement1 = "TEST");
- myElement2 는 default 값(5) 사용
default 값이 없으면 반드시 값을 기술해야하지만 있다면 생략할 수 있다.
public @interface myAnno {
String value();
}
public class Test {
@myAnno("이름생략가능")
public void doing() {
// ...
}
}
하나의 멤버만있고 멤버의 이름이 value 인경우 사용시 멤버이름을 지정하지 않아도 된다.
java.lang.annotation
- @Inherited
- 해당 어노테이션이 자손 클래스에도 상속되도록 한다.
- @Documented
- javadoc으로 api문서를 작성시 해당 어노테이션에 대한 설명도 포함하도록 지정해주는것
- @Retention (RetentionPolicy.RUNTIME / SOURCE / CLASS)
- 어느시점까지 어노테이션의 메모리를 가져갈지 설정한다.(어노테이션의 라이프사이클 정의)
- RUNTIME : 컴파일 이후에도 JVM에 의해 참조 가능
- SOURCE : 컴파일 이후 없어짐
- CLASS : 컴파일러가 클래스를 참조할 때가지 유효
- 어느시점까지 어노테이션의 메모리를 가져갈지 설정한다.(어노테이션의 라이프사이클 정의)
- @Target : 사용될 위치 지정
- ElementType.METHOD
- ElementType.PACKAGE
- ElementType.TYPE
- class, interface, annotation, enum 에 적용 가능
- ElementType.ANNOTATION_TYPE
- ElementType.FIELD
- 필드, enum 상수
- ElementType.CONSTRUCTOR
- ElementType.LOCAL_VARIABLE
- ElementType.MODULE
- 자바 8부터 추가
- ElementType.TYPE_PARAMETER
static class Test<@TypeAnno T> { }
-
ElementType.TYPE_USE
타입 파라미터를 포함해서 타입을 포함하는 모든곳에 사용 가능
@TypeAnno public class App { public static void main(@TypeAnno String[] args) throws @TypeAnno RuntimeException { List<@TypeAnno String> names = Arrays.asList("test"); } static class Test<@TypeAnno T> { // 타입 파라미터, 타입 public static <@TypeAnno C> void print(@TypeAnnoC c) { } } }
- ElementType.TYPE_PARAMETER
- @Repeatable
- 이전에는 어노테이션의 중복이 불가능했지만 이 어노테이션을 이용하면 가능하다.
- @Repeatable() 안에 여러 어노테이션(중복애노테이션)을 감싸고 있을 컨테이너 어노테이션 타입을 선언
- 이때, 컨테이너어노테이션이 가지고 있는 @Retention과 @target 정보는 중복 어노테이션보다 같거나 넓어야 한다.
- 쓰는방법
-
중복애노테이션타입으로 가져오는 방법
Words[] words = App.class.getAnnotationsByType(Words.class); Arrays.stream(words).forEach(w -> { System.out.println(w.value()); });
-
컨테이너 타입으로 가져오는 방법
MyRepeatedAnnos container = App.class.getAnnotation(MyRepeatedAnnos.class); Arrays.stream(container.value()).forEach(w -> { System.out.println(w.value()); });
-
- 사용 예제
@Retention(RetentionPolicy.RUNTIME) @Repeatable(MyRepeatedAnnos.class) @interface Words { String word() default "Hello"; int value() default 0; } @Retention(RetentionPolicy.RUNTIME) @interface MyRepeatedAnnos { // 컨테이너 어노테이션 Words[] value(); } public class App { @Words(word = "First", value = 1) @Words(word = "Second", value = 2) public static void newMethod() { App obj = new App(); try { Class<?> c = obj.getClass(); // Obtain the annotation for newMethod Method m = c.getMethod("newMethod"); // Display the repeated annotation Annotation anno = m.getAnnotation(MyRepeatedAnnos.class); System.out.println(anno); } catch (NoSuchMethodException e) { System.out.println(e); } } public static void main(String[] args) { newMethod(); } }
- 이전에는 어노테이션의 중복이 불가능했지만 이 어노테이션을 이용하면 가능하다.
java.lang
- @Deprecated
- 해당 메소드를 사용하지 않도록 유도한다. (컴파일 경고)
- @Override
- 메소드가 오버라이드 되었는지 검증한다. 만약, 부모 클래스 또는 구현해야할 인터페이스에서 해당 메소드를 찾을 수 없다면 컴파일 에러 발생
- @SuppressWarnings
- 컴파일 경고를 무시하도록 하는 기능
애노테이션 프로세서
Annotation Processing은 javac에 속한 빌드툴로, 컴파일타임에 어노테이션을 분석하여 붙여진 어노테이션에 따라 클래스를 만들어 낸다.
어떤 순서로 동작하는가?
- 어노테이션 클래스를 생성한다.
- 어노테이션 Parser 클래스를 생성한다.
- 어노테이션을 사용한다.
- 컴파일하면 어노테이션 Parser가 어노테이션을 처리한다.
- 자동생성된 클래스가 빌드 폴더에 추가된다.
그리고, Annotation Parser classes 는 프로젝트를 컴파일 할 때만 필요하고, 빌드가 끝나면 쓰이지 않는다.
ServiceLoader
인터페이스의 구현체를 모르는데 jar 파일만 바꿔끼어 동작할 수 있게,
즉, 해당 인터페이스 타입을 구현한 구현체의 인스턴스를 가져 올 수 있게 해주는 것
Reference
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
https://www.nextree.co.kr/p5864/