어노테이션
- 어노테이션
- Annotation
- Builtln Annotation
- 사용자 정의 Annotation
- Annotation 접근
- Annotation Processing Tool
- Reference
- 문서에 대하여
Annotation
Annotation 이란
- 메타데이터, 한 마디로 데이터의 데이터다
- 어노테이션이 나오기 전까지 메타데이터는 프로퍼티 파일과 XML파일을 활용하였다.
- 어노테이션은 주로 데이터를 문서화하거나, 컴파일 타임이나 런타임시에 원하는 동작을 수행할 수 있도록 하는 데 사용된다.
- 닷넷프레임워크 1.0에서 어트리뷰트라는 기능을 제공하자, 자바에서 어노테이션을 통한 메타데이터 활용 기술을 제공했다.
Annotation의 장점
- 코드의 가독성 증대
- 관련 코드 곁에 메타데이터를 설정할 수 있으므로 코드의 가독성 증대 된다.
- 개발 효율성 증대
- 복잡한 XML 스키마를 파악하지 않아도 되며, 개발시 개발 툴과 컴파일러의 도움을 받을 수 있으므로 개발 효율성이 증대 된다.
- 별도의 파서를 적용하지 않고도 간단히 런타임 시에 활용할 수 있는 편리함이 있다.
- JUnit, Framework, Permission Module 에서 사용
Annotation을 반대하는 사람들의 의견
- 메타정보와 소스의 결합자체를 못마땅하다.
- 메타데이터는 그 자체로 코드에 독립적이어야 하며 한 곳에 모여져서 어플리케이션의 구성을 한눈에 알 수 있어야 바람직하다는 주장.
- 어노테이션은 코드를 이용해서 검증하는게 가능하지만 XML의 DTD나 스키마를 이용하는 검증 방식에 비해서 훨씬 불편하다.
Annotation의 단점
- 어노테이션 처리시 리플렉션을 통해 해당 클래스를 분석해야 하는 오버헤드가 있다.
- XML 파일을 이용하는 방법도 오버헤드가 있으므로, 경우에 따라서 어노테이션이 더 빠를 수도 있음.
- 어노테이션은 모듈이나 어플리케이션 전반적인 메타데이터를 설정할 수 없다.
- 어노테이션의 범위는 클래스나 패키지 레벨로 한정되기 때문에 여러 클래스에 걸친 공통적인 설정이나 모듈레벨 설정이 어렵다.
- 웹 어플리케이션 개발시 서블릿 필터나, 인터셉터를 이용해서 문제 해결이 가능함.
XML과 어노테이션같이 쓰기
- 대부분의 일반적인 웹 어플리케이션에 적합한 방법 이다.
- 어플리케이션 상에서 디자인 타임에 결정되는 부분에 대해서는 어노테이션을 사용하는 것이 좋고,
실제 디플로이 환경에 따라 바뀔 수 있는 부분의 경우 XML을 사용하여 표기하는 것이 좋다.
Annotation 형태
- Marker Annotation
- 이름만 있는 어노테이션
- @AnnotationName
- Single-Element Annotation
- 하나의 원소만을 가지고 있는 어노테이션
- @AnnotationName(elementValue)
- Normal Annotation
- 여러 개의 원소를 갖는 어노테이션
- @AnnotationName(element=value, element=value, ...)
Builtln Annotation
Standard Annotations
- @Deprecated
- 특정 클래스나 인터페이스, 메소드, 필드 등이 앞으로 더이상 사용되지 말아야 한다는 것을 경고하기 위해서 사용.
- 컴파일러는 deprecated된 메소드나 클래스 혹은 변수를 사용할 때마다 경고를 발생시킨다.
- @Override
- 메소드에 대해서만 사용되어야 한다. (클래스, 패키지 선언, 기타 구조체는 안된다.)
- 수퍼클래스에서 메소드를 오버라이드한다는 것을 나타내며, 슈퍼클래스의 메소드를 재정의하지 못하면 컴파일 에러가 발생.
- @SuppressWarning
- 컴파일러에게 경고를 하지 않도록 지시 한다.
- all, deprecation, unchecked, fallthrough, path, serial, finally 등의 금지 옵션이 있다.
- @Target
- 어노테이션을 정의 시 어노테이션이 사용 가능한 대상을 지정한다.
- ElementType의 상수로 정의
| ElementType 상수 | 의미 |
|---|
| ANNOTATION_TYPE | 어노테이션 형 |
| CONSTRUCTOR | 생성자 |
| FIELD | enum 상수를 포함한 필드(멤버변수) |
| LOCAL_VARIABLE | 지역변수 |
| METHOD | 메소드 |
| PACKAGE | 패키지 |
| PARAMETER | 매개변수 |
| TYPE | 클래스, 인터페이스(어노테이션 형 포함), 열거형 |
@Target({ElementType.TYPE,
ElementType.METHOD,
ElementType.CONSTRUCTOR,
ElementType.ANNOTATION_TYPE})
- @Retention
- 어노테이션 정보의 유지 범위를 설정
- RetentionPolicy의 상수로 정의
- @Retention(RetentionPolicy.SOURCE)
| RetentionPolicy 상수 | 의미 |
|---|
| SOURCE | 어노테이션 정보를 클래스 파일에 저장되지 않고 소스를 처리하는 도구에서만 사용. |
| CLASS | 어노테이션 정보를 컴파일러는 클래스 파일에 저장하지만 VM에서는 읽지 않고 버려짐. |
| RUNTIME | 어노테이션 정보를 컴파일러는 클래스 파일에 저장하고, VM에서 저장된 정보를 읽음. |
- @Documented : @Documented 해당 어노테이션을 Javadoc에 포함한다.
- @Inherited 서브 클래스가 부모 어노테이션을 상속받도록 한다.
사용자 정의 Annotation
- 프로그래머가 정의하는 어노테이션으로 class 형태로 만들어진다.
- 어노테이션의 선언은 @interface 로 한다.
- 이름 앞에 '@' 문자가 오는 것 외에는 기본적으로 인터페이스를 선언하는 것과 동일(메소드들의 내용은 없고 형태만 선언)
- default 가 찍히지 않은 메소드는 필수로 입력해야 한다.
@Retention(RetentionPolicy.RUNTIME)
public @interface Maker {
int num();
String name();
String id();
String date() default "unsigned";
}
@Maker(num=1, name="김정식", id="oramaster")
public class UseMaker {
/**
* class.getAnnotations() 와 같이 어노테이션을 파싱할수 있다.<br/>
* 물론 어노테이션 인터페이스에서 @Retention(RetentionPolicy.RUNTIME) 를 선언해 줘야지만 가능하다<br/>
*
*/
public static void main(String args[]) {
for (Annotation a: UseMaker.class.getAnnotations()) {
System.out.println("Annotation : " + a);
}
}
}
결과
Annotation : @com.oracleclub.sample.annotation.Maker(date=unsigned, num=1, name=김정식, id=oramaster)
Annotation 접근
- 실행중인 프로그램에서 Annotation에 접근하는 방법을 알아보자
- 인터페이스 java.lang.reflect.AnnotateElement를 통해서 실행중인 Annotation에 접근 할 수 있다.
- boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
- Annotation 존재 여부를 반환한다.
- Annotation이 존재하면 true, 존재하지 않으면 false 반환
- <T extends Annotation> T getAnnotation(Class<T> annotationType)
- Annotation[] getAnnotations()
- 존재하는 모든 Annotation들을 반환 (private 제외)
- Annotation[] getDeclaredAnnotations()
- 존재하는 모든 Annotation들을 반환 (private 포함)
간단한 예제
SimpleTest.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SimpleTest { }
- SimpleTest를 사용하는 테스트 케이스 UseSimpleTest 생성
UseSimpleTest.java
public class UseSimpleTest {
@SimpleTest
public void test1(){
System.out.println(" # test1 ");
}
@SimpleTest
public void test2() throws Exception{
throw new Exception(" # 에러 발생 ");
}
@SimpleTest
public void test3(){
System.out.println(" # test3 ");
}
}
- Annotation에 접근해서 테스트 해보자.
RunnerSimpleTest.java
public static void main(String[] args) throws Exception{
RunnerSimpleTest.runner("com.oracleclub.sample.annotation.UseSimpleTest");
}
private static void runner(String className) {
int pass = 0;
int fail = 0;
Object testObject = null;
try{
testObject = Class.forName(className).newInstance();
}catch(Exception ce){
System.out.println(className+"를 찾을 수 없습니다. ");
}
for(Method m : testObject.getClass().getMethods()){
if(m.isAnnotationPresent(SimpleTest.class)){
try{
m.invoke(testObject, null);
pass++;
}catch(Exception e){
System.out.println(m.getName()+" 테스트 실패 : "+e.getCause());
fail++;
}
}
}
System.out.println(" # 테스트 종료 -> 성공 : "+pass+", 실패 : "+fail);
}
//실행결과
# test1
test2 테스트 실패 : java.lang.Exception: # 에러 발생
# test3
# 테스트 종료 -> 성공 : 2, 실패 : 1
- Annotation의 소스를 체크하고 컴파일을 처리해줌
- JDK 5에는 어노테이션을 읽고 이해하는 API가 표준화되어 있지 않아 JDK에 들어있는 썬 고유의 라이브러리를 사용했고, 자바 SE 6에 기본으로 포함되었다.
- JDK 5에는 APT(Annotation Processing Tool)라는 패키지가 포함되어, JSR 269(JSR 269 Pluggable Annotation Processing API)가 나오기 전까지 임시로 그 역할을 대신했다.
- Apache Ant에서는 APT에 대응하는 앤트 타스크를 따로 두어 지원하였다. http://ant.apache.org/manual/CoreTasks/apt.html에서 확인할 수 있다.
Reference
문서에 대하여
- 작성일자 : 김정식
- 작성자 : 2008년 10월 31일
- 이 문서는 오라클클럽 에서 작성하였습니다.
- 이 문서를 다른 블로그나 홈페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.