어노테이션(Annitation)은 메타데이터(metadata)
메타데이터란 애플리케이션이 처리해야 할 데이터가 아니라
컴파일 과정과 실행과정에서 코드를 어떻게 컴파일 하고 처리할 것인지를 알려주는 정보
@AnnotationName
- 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
- 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
- 실행 시 (런타임 시) 특정 기능을 실행하도록 정보를 제공
대표 ex) @Override -> 메소드 선언 시 사용하는데, 메소드가 오버라이드(재정의) 된 것임을 컴파일러에게 알려줌
빌드시 자동으로 XML설정 파일을 생성하거나
배포를 위해 JAR압축 파일을 생성하는데 사용
실헹 시 클래스의 역할을 정의
어노테이션 타입 정의와 적용
어노테이션 타입을 정의하는 방법은 인터페이스를 정의하는것과 유사
public @interface AnnotataionName {}
사용 시
@AnnotationName
엘리먼트(element)를 멤버로 가질수 있다, 각 엘리먼트는 타입과 이름으로 구성되며 디폴트 값을 가질 수 있다
public @interface AnnotationNmae{
타입 elementName() [deafault 값];
}
엘리먼트의 타입
int, double String 열거타입 class 타입 배열 타입
어노테이션은 기본 엘리먼트인 value를 가질수 있다
String value
Value 엘리먼트를 가진 어노테이션을 코드에서 적용 할 때에는 다음과 같이 값만 기술할수 있다.
이 값은 기본 엘리먼트인 value 값으로 자동 설정
어노테이션 적용 대상
java.lang.annotation.ElementType 열거 상수로 다음과 같이 정
ElementType 열거 상수 | 적용 대산 |
Type | 클래스, 인터페이스, 열거 타입 |
ANNOTATION_TYPE | 어노테이션 |
FIELD | 필드 |
CONSTRUCTOR | 생성자 |
METHOD | 메소드 |
LOCAL_VARIABLE | 로컬변수 |
PACKAGE | 패키지 |
어노테이션 적용될 대상을 지정할 때에는 @Target 어노텡션을 사용한다. @Target 기본 엘리먼트인 value는 ElementType을 배열을 값으로 가진다.
@Target(ElementType, ElementType.FIELD, ElementType.METHOD)
public @interface AnnotationName{
}
다음과 같이 클래스, 필드, 메소드만 어노테이션을 적용 할 수있고 생성자는 적용 할 수없다
@AnnotationName
public class ClassName{
@AnnotaitonName
private STring filedName;
//@AnnotationName -> @Target에 CONSTRUCT가 없어 생성자는 적용 못하
public ClassName(){}
@AnntationName
public void methodName(){}
}
어노테이션 유지 정책
어노테이션 정의시 한가지 더 추가해야할 내영은 사용 용도에 따라 @AnnotationName을 어느 범위 까지 유지할 것인지 가정
소스까지인지, 컴파일된 클래스까지, 런타임시 유지
java.lang.annotation.RetentioPolicy 열겨 상수로 다음과 같이 정의
RetentionPolicy 열거 상수 | 설명 |
SOURCE | 소스상에서만 어노테이션 정보를 유지한다. 소스코드를 분석할때만 의미가 있으며 바이트 코드 파일에는 정보가 남지 않는다 |
CLASS | 바이트 코드 파일 까지 어노테이션 정보를 유지한다. 하지만 리플렉션이용해서 어노테이션 정보를 얻을 수 없다 |
RUNTIME | 바이트 코드 파일까지 어노테이션 정보를 유지하지면서 리플렉션을 이용해서 런타임 시에 어노테이션 정보를 얻을수 있다. |
리플렉션(Reflection) 이란?
런타임시에 클래스의 메타 정보를 얻는 기능을 말한다.
클래스가 가지고 있는 필드가 무엇인지
어떤 생성자를 갖고 있는지
어떤 메소드를 가지고 있는지
적용된 어노테이션이 무엇인지 알아내는 것 - 리플렉션
어노테이션 유지 정책을 지정할때에는 @Retention 어노테이션을 사용한다.
@Retention의 기본 엘리먼트인 value 는 RetentionPolicy 타입 위 세가지 상수 중 하나 지정
@Target({ElemenType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUTIME)
pulic @interface AnnotationName{}
-> 코드 자동 생성 툴을 개발하지 않는 이상, 어노테이션은 대부분 런타임 시점에 사용하기 위한 용도롤 만들어짐
런타임 시 어노테이션 정보 사용 하기
런타임 시에 어노 테이션이 적용 되었는지 확인하고 엘리먼트 값을 이용해서 특정 작업을 수행하는 방법
어노테이션 자체는 아무런 동작을 가지지 않는 단지 표식
리플렉션을 이용해서 어노테이션의 적용 여부
엘리먼트 값을 읽고 적절히 처리
java.lang.Class : 클래스에 적용된 어노테이션 정보 얻기
Class 다음 메소드 이용, java.lang.reflect 패키지 field, constructor, Method 타입 배열 : 필드, 생성자, 메소드 적용된 정보 얻기
리턴 타입 | 메소드명(매개 변수) | 설명 |
Field[] | getFields() | 필드 정보를 Field 배열로 리턴 |
Constructor[] | getConstructors() | 생성자 정보를 Constructor 배열로 리턴 |
Method[] | getDeclaredMethods() | 메소드 정보를 Method 배열로 리턴 |
그 다음 Class, Field, Constructor, Method 가 가지고 있는 다음 메소드를 호출해서 적용된 어노테이션 정보를 얻을수 잇다
리턴 타입 | 메소드명(매개 변수) |
boolean | isAnnotationPresent(Class<? extends Annotation) annotationClass) |
지정한 어노테이션이 적용 되었는지 여부, Class에서 호출 했을 때 상위 클래스에 적용된 경우에도 true를 리턴한다. | |
Annotation | getAnnotation(Class<T> annotationClass) |
지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 어노테이션을 리턴한다. |
|
Annotation[] | getAnnotations() |
적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함한다. 적요오딘 어노테이션이 없을 경우 길이가 0인 배열을 리턴한다 | |
Annotation[] | getDeclaredAnnotations() |
직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않는다. |
@Target([ElementType.METHOD])
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
String value() default "-";
int number() default 15;
}
@Target -> 메소드 만 적용
@Retention 은 런타임 시 까지 어노테이션 정보를 유지
기본 엘리먼트 value는 구분선에 사용될 문자, number는 반복 출력 횟수
각각 디폴트 값으로 "-" 와 15
Public class Service {
@PrintAnnotation
public void method1(){
System.out.println("실행 내용1");
}
@PrintAnnotation("*")
public void method2(){
System.out.println("실행 내용2");
}
@PrintAnnotation(value="#", number=20)
public void method3() {
System.out.println("실행 내용3");
}
}
리픅렉션을 이용해서 Service 클래스에 적용된 어노테이션 정보를 읽고 엘리먼트 값에 따라 출력할 문자와 출력 횟수를 콘솔에 출력한 후, 해당 메소드를 호출한다
method.invoke(new Service())는 Service 객체를 생성하고 생성된 Service 객체의 메소드를 호출하는 코드
public class PrintAnnotationExample {
public static void main(String[] args){
//Service 클래스로부터 메소드 정보를 얻음
Method[] declaredMethods = Service.class.getDeclareMethods(); /*Service 클래스에 선언된 메소드 얻기 리플렉션*/
//Method 객체를 하나씩 처리
for(Method method : dlecareMethods) {
//PrintAnnotation이 적용 되었는지 확인
if(method.isAnnotationPresent(PrintAnnotation.class)){
//PrintAnnotation 객체 얻기
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
//메소드 이름 출력
System.out.println("["+method.getName()+"]");
//구분선 출력
for(int i =0; i<printAnnotation.number(); i++){
System.out.print(printAnnotation.value());
}
System.out.println();
try{
//메소드 호출
method.invoke(new Service());
} catch (Exception e){
System.out.println();
}
}
}
}
}
'Back-end > 이것이 자바다[신용권 한빛미디어]' 카테고리의 다른 글
타입 변환과 다형성 (0) | 2021.10.04 |
---|---|
상속 (0) | 2021.10.04 |
Getter 와 Setter 메소드 (0) | 2021.10.03 |
접근제한자 (0) | 2021.10.03 |
패키지 (0) | 2021.10.03 |