어노테이션(Annotation)
프로그램에게 추가적인 정보를 제공해주는 메타데이터(metadata)이다.
어노테이션 용도
컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공
ex) 메소드가 재정의 되었는지 확인할 수 있다. (@Override)
소프트웨어 개발 툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보를 제공
ex) XML 설정 파일을 자동 생성하거나 배포를 위한 JAR 압축 파일을 자동 생성 할 수 있다.
실행시(런타임시) 특정 기능을 실행하도록 정보를 제공
ex) 객체가 애플리케이션 내부에서 해야할 역할을 정의할 수 있다. (서블릿, 컨트롤러, ...)
어노테이션 타입 정의
소스 파일 생성 : AnnotationName.java
소스 파일 내용
public @interface AnnotationName
{
}
어노테이션 타입 적용
@AnnotationName
어노테이션의 엘리먼트(element)멤버
어노테이션을 코드에 적용할 때 외부의 값(개발자가 입력한 값)을 입력받을 수 있도록 하는 역할
엘리먼트 선언
public @interface AnnotationName
{
타입 elementName() [default 값]; //엘리먼트 선언
}
엘리먼트의 타입은 기본타입과 참조타입을 모두 사용할 수 있다.
어노테이션 적용시 엘리먼트값을 지정하는 방법
@AnnotationName(elementName1="값", elementName2=3);
또는
@AnnotationName(elementName1="값");
기본 엘리먼트 value
public @interface AnnotationName
{
String value(); //기본 엘리먼트 선언
int elementName() default 5;
}
어노테이션을 적용할 때 엘리먼트명을 생략가능
@AnnotationName("값");
두 개 이상의 속성을 기술할 때에는 value = 값 형태로 기술
@AnnotationName(value="값", elementName=3);
어노테이션 적용 대상
코드상에서 어노테이션을 적용할 수 있는 대상
java.lang.annotation.ElementType 열거 상수로 정의되어 있음
@AnnotationName
public class ClassName
{
@AnnotationName
private String fieldName;
//@AnnotationName (x)
public ClassName() { }
@AnnotationName
public void methodName() { }
}
어노테이션 적용 대상 지정 방법
- @Target 어노테이션으로 적용 대상 지정
- @Target의 기본 엘리먼트인 value의 타입은 ElementType 배열
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface AnnotationName
{
}
어노테이션 유지 정책
어노테이션 적용 코드가 유지되는 시점을 지정하는 것
java.lang.annotation.RefentionPolicy 열거 상수로 정의되어 있음
RetentionPolicy 열거 상수 | 설명 |
SOURCE | 소스상에서만 어노테이션 정보를 유지한다. 소스 코드를 분석할때만 의미가 있으며, 바이트 코드 파일에는 정보가 남지 않는다. |
CLASS | 바이트 코드 파일까지 어노테이션 정보를 유지한다. 하지만 리플렉션을 이용해서 어노테이션 정보를 얻을 수는 없다. |
RUNTIME | 바이트 코드 파일까지 어노테이션 정보를 유지하면서 리플렉션을 이용해서 런타임에 어노테이션 정보를 얻을 수 있다. |
리플렉션(Reflection) : 런타임에 클래스의 메타 정보를 얻는 기능
클래스가 가지고 있는 필드, 생성자, 메소드, 어노테이션의 정보를 얻을 수 있다.
런타임시에 이노테이션 정보를 얻으려면 유지 정책을 RUNTIME으로 설정해야함.
유지 정책 지정 방법
- @Retention 어노테이션으로 유지 정책을 지정
- @Retention의 기본 엘리먼트인 value의 타입은 RetentionPolicy
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention (RetentionPolicy.RUNTIME)
public @interface AnnotationName
{
}
런타임시에 어노테이션 정보 사용하기
- 클래스에 적용된 어노테이션 정보 얻기 : 클래스.class의 어노테이션 정보를 얻는 메소드를 이용
- 필드, 생성자, 메소드에 적용된 어노테이션 정보 얻기 : 클래스.class의 다음 메소드를 이용해서 java.lang.reflect 패키지의 Field, Constructor, Method 클래스의 배열을 얻어냄
- Field, Constructor, Method의 어노테이션 정보를 얻는 메소드를 이용
어노테이션 정보를 얻기 위한 메소드
예제)
package sec15.exam01_annotation;
import java.lang.annotation.*;
//적용할 대상
@Target({ElementType.METHOD}) //method타입해만 적용 가능
//어노테이션의 정책을 어디까지 유지할 것인지
@Retention(RetentionPolicy.RUNTIME) //실행할때 추가적인 정보를 가져와서 사용하곘다.
public @interface PrintAnnotation
{
//1. 구분선으로 사용될 문자의 정보
//2. 구분선 문자가 몇번 출력이 될것인가
String value() default "-"; //타입이 String인 value라고 하는 엘리먼트 / 기본엘리먼트 : 구분선에 사용될 문자 / - 를 default값으로 가짐
int number() default 15; //기본값으로 15개가 나오도록
}
package sec15.exam01_annotation;
public class Service
{
@PrintAnnotation //기본값이 사용됨
public void method1()
{
System.out.println("실행 내용1");
}
@PrintAnnotation("*") //구분선의 모양을 *로 바꿈
public void method2()
{
System.out.println("실행 내용2");
}
@PrintAnnotation(value = "#", number=20) // 구분선의 모양은 #, 갯수는 20개로 바꿈
public void method3()
{
System.out.println("실행 내용3");
}
}
package sec15.exam01_annotation;
import java.lang.reflect.*;
public class PrintAnnotationExample
{
public static void main(String[] args)
{
Method[] declaredMethods = Service.class.getDeclaredMethods(); //모든 Methods에 대한 정보를 배열로 받아옴
for(Method method : declaredMethods) //향상된 for문 : for(타입변수 : 배열) 배열에서 가져올 첫번째 값이 존재하는지 평가하여 가져올 값이 존재하면
//해당 값을 타입 변수에 저장하고 실행문을 실행한다. 블록 내무의 실행문이 모두 실행되면 다시 루프를 돌아 배열에서 가져올 다음 값이 존재하는지 평가. 쭉 반복
{
/* (1)
if(method.isAnnotationPresent(PrintAnnotation.class)) //isAnnotationPresent : annotation이 적용이 되어있는지
{
System.out.println(method.getName() + "에는 적용되어 있음"); //method 이름을 return
}
else
{
System.out.println(method.getName() + "에는 적용되어 있지 않음");
}
*/
/* (2)
if(method.isAnnotationPresent(PrintAnnotation.class))
{
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class); //메소드에 적용된 annotation의 값을 가져와서 저장
System.out.println(method.getName() + ": ");
System.out.println(printAnnotation.value());
System.out.println(printAnnotation.number());
System.out.println();
}
*/
if(method.isAnnotationPresent(PrintAnnotation.class))
{
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
//메소드 이름 출력
System.out.println("[" + method.getName() + "]");
//구분선 출력
for(int i = 0; i<printAnnotation.number(); i++) //number 엘리먼트의 값만큼 반복됨
{
System.out.print(printAnnotation.value());
}
System.out.println();
//메소드 호출
try
{
method.invoke(new Service());
}
catch (Exception e)
{
}
//method.invoke(new Service()); 와
//Service service = new Service();
//service.method1();
//와 같음
System.out.println();
}
}
}
}
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
[JAVA] 부모 생성자 호출(super(...)) (0) | 2021.07.05 |
---|---|
[JAVA] 상속 (0) | 2021.07.05 |
[JAVA] Getter와 Setter (0) | 2021.07.01 |
[JAVA] 접근 제한자 (0) | 2021.06.30 |
[JAVA] 패키지(package) (0) | 2021.06.29 |