Search
Duplicate
😀

12. 어노테이션(Annotation)

태그
어노테이션
어노테이션 프로세서

Annotation

어노테이션은 사전적 의미로는 주석이라는 뜻이다. 자바에서 사용될 때의 어노테이션은 코드 사이에 주석처럼 쓰여서 특별한 의미, 기능을 수행하도록 하는 기술이다. 즉, 프로그램에게 추가적인 정보를 제공해주는 메타데이터(meta data: 데이터에 대한 설명을 의미하는 데이터)라고 볼 수 있다.

Annotation의 용도

컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공
소프트웨어 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보 제공
실행시(런타임시)특정 기능을 실행하도록 정보를 제공
자바에서 제공되는 어노테이션은 크게 2가지로 구성되어져 있다.
1.
하나는 자바코드를 작성할 때, 사용되는 어노테이션(표준 어노테이션)
2.
다른 하나는 어노테이션을 정의하기 위해 필요한 어노테이션이다(메타 어노테이션)

표준 Annotation

1.
@Override
오버라이드를 할 때 사용되며, 메소드가 오버라이드 되었는지 알려주는 역할을 한다.
만약 상위(부모) 클래스(또는 인터페이스)에서 해당 메서드를 찾을 수 없다면 컴파일 에러를 발생 시킵니다.
생략이 가능하다. (이 어노테이션이 존재하는 이유는 미연의 실수를 방지하기 위함)
2.
@Deprecated
해당 메서드가 더 이상 사용되지 않음을 표시합니다.
만약 사용할 경우 컴파일 경고를 발생 키십니다.
호환성 때문에 존재하는 어노테이션이다.
@Deprecated public Date(int year, int month, int date) { this(year, month, date, 0, 0, 0); }
Java
복사
Date클래스를 살펴보면 생성자가 deprecated가 존재한다는 것을 확인 할 수 있다. 읽어보면 이 클래스는 더 이상 사용하지 말고, Calender클래스를 사용을 권장하고 있다
그렇다면 이 클래스를 자바 자체에서 삭제해 버리면 기존에 Date클래스를 통해 작성한 프로그램이 동작하지 않는다.
3.
@SuppressWarnings
선언한 곳의 컴파일 경고를 무시하도록 합니다.
4.
@SafeVarargs
Java7 부터 지원하며, 제너릭 같은 가변인자의 매개변수를 사용할 때의 경고를 무시합니다.
5.
@FunctionalInterface
Java8 부터 지원하며, 함수형 인터페이스를 지정하는 어노테이션입니다.
만약 메서드가 존재하지 않거나, 1개 이상의 메서드(default 메서드 제외)가 존재할 경우 컴파일 오류를 발생 시킵니다.

메타 Annotation

1.
@Retention
자바 컴파일러가 어노테이션을 다루는 방법을 기술하며, 특정 시점까지 영향을 미치는지를 결정합니다.
종류는 다음과 같습니다.
RetentionPolicy.SOURCE : 컴파일 전까지만 유효. (컴파일 이후에는 사라짐)
RetentionPolicy.CLASS : 컴파일러가 클래스를 참조할 때까지 유효.
RetentionPolicy.RUNTIME : 컴파일 이후에도 JVM에 의해 계속 참조가 가능. (리플렉션 사용)
2.
@Target
어노테이션이 적용할 위치를 선택합니다.
종류는 다음과 같습니다.
ElementType.PACKAGE : 패키지 선언시
ElementType.TYPE : 타입 선언시
ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언시
ElementType.CONSTRUCTOR : 생성자 선언시
ElementType.FIELD : 멤버 변수 선언시
ElementType.LOCAL_VARIABLE : 지역 변수 선언시
ElementType.METHOD : 메서드 선언시
ElementType.PARAMETER : 매개변수 선언시
ElementType.TYPE_PARAMETER : 매개변수 타입 선언시
ElementType.TYPE_USE : 타입 사용시
3.
@Documented
해당 어노테이션을 Javadoc에 포함시킵니다.
4.
@Inherited
어노테이션의 상속을 가능하게 합니다. (상속받은 클래스에도 어노테이션이 유지되도록)
5.
@Repeatable
Java8 부터 지원하며, 연속적으로 어노테이션을 선언할 수 있게 해줍니다.

Custom Annotation 정의하기

1.
어노테이션 선언
2.
클래스에 어노테이션을 배치
3.
코드가 실행되는 중에 Reflection을 이용하여 추가정보를 획득하여 기능 실시

1. 어노테이션의 선언

어노테이션을 적용할 때는 어노테이션이 어디에 적용되며, 언제까지 어노테이션 소스가 유지될 것인지를 설정해 주어야 한다.
어노테이션의 요소는 반환값이 있고, 매개변수는 없는 추상 메서드의 형태이다.
@Target({ElementType.[적용대상]}) @Retention(RetentionPolicy.[정보유지되는 대상]) public @interface [어노테이션명]{ public 타입 elementName() [default] ... }
Java
복사
@Target에는 어떠한 값(ex : 클래스, 필드, 메서드 ,,,)에 어노테이션을 적용할 것인지 나타낼 수 있다.
@Retention에는 어노테이션 값들을 언제까지 유지할 것인지 값을 입력한다. 보통 어노테이션은 Runtime 시에 많이 사용하므로 대부분의 어노테이션의 Retention 값은 RUNTIME으로 되어있다.

2. 어노테이션의 배치 및 사용

어노테이션의 사용은 클래스를 참고하는 소스의 흐름상에서 Reflection을 사용하는 방법을 통해서 어노테이션 값을 활용하도록 한다. 말로는 설명이 애매하니 예제 소스를 작성하도록 한다.
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "-"; int number() default 15; } class MyService { @MyAnnotation public void method1() { System.out.println("실행내용1"); } @MyAnnotation("*") public void method2() { System.out.println("실행내용2"); } @MyAnnotation(value = "*", number = 20) public void method3() { System.out.println("실행내용3"); } } public class MyMain { public static void main(String[] args) { Method[] methodList= MyService.class.getMethods(); for(Method m : methodList) { if(m.isAnnotationPresent(MyAnnotation.class)) { System.out.println(m.getName()); MyAnnotation annotation=m.getDeclaredAnnotation(MyAnnotation.class); String value=annotation.value(); int number=annotation.number(); for(int i=0;i<number;i++) { System.out.print(value); } System.out.println(); } } } }
Java
복사
필드, 생성자, 메소드에 적용된 어노테이션 정보는 위에서 처럼 .class로부터 얻을 수 있다.
클래스.class의 다음 메소드를 이용해서
java.lang.reflect 패키지의 Field, Constructor, Method 클래스의 배열을 얻어냄
[어노테이션 정보를 얻기 위한 메서드]

Annotation Processing

어노테이션 프로세서는 자바 컴파일러의 컴파일 단계에서 애노테이션 정보를 스캔하고 처리하는 Hook이다.
쉽게 이야기하면, 컴파일 도중에 애노테이션을 만나면 특정한 동작을 하도록 만들어진 코드를 말한다.
어노테이션 프로세서는 소스코드 파일(.java)이나 클래스 파일(.class)을 입력으로 받아서 새로운 소스코드 파일 혹은 클래스 파일을 생산해낸다.

어노테이션 프로세서 사용 예제

Lombok : 어노테이션을 이용해서 재사용 가능한 코드를 생산해주는 라이브러리
AutoService : java.util.ServiceLoader 용 파일 생성 유틸리티
@Override
Dangger 2 : 컴파일 타임 DI 제공

사용 예시 - Lombok

Lombok 사용 전
public class Book { private String title; private String author; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
Java
복사
Lombok 사용 후
import lombok.Getter; import lombok.Setter; @Getter @Setter public class Book { private String title; private String author; }
Java
복사
단지 애노테이션만 달았을 뿐인데, 클래스 파일에 getter, setter가 추가되어 있다!
이는 롬복 라이브러리 내부에 작성된 애노테이션 프로세서가 컴파일 타임에 내 코드에 붙은 애노테이션을 읽어서 그에 맞게 코드를 재생산해냈기 때문에 일어난 일이다.

Custom 어노테이션 프로세싱

1.
어노테이션 클래스를 생성한다.
2.
어노테이션 파서 클래스(어노테이션 프로세서)를 생성한다.
3.
어노테이션을 사용한다.
4.
컴파일하면, 어노테이션 파서가 어노테이션을 처리한다.
5.
자동 생성된 클래스가 빌드 폴더에 추가된다.
자세한 어노테이션 프로세싱 방법은 너무 복잡해서 아래 링크를 첨부함

어노테이션 프로세서의 장점

빠르다
어노테이션 프로세서는 실제로 javac 컴파일러의 일부이므로 모든 처리가 런타임보다는 컴파일시간에 발생합니다.
리플랙션을 사용하지 않는다
자바의 리플렉션은 런타임에 많은 예외를 발생시킵니다. 또한 리플랙션은 비용이 큰 작업입니다.
어노테이션 프로세서는 리플랙션없이 프로그램의 의미구조를 알 수 있게 해줍니다.
Boilerplate code를 생성해줍니다.
Boilerplate: 재사용가능한 것.