07
02
728x90

어노테이션(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();
			}
		}
	}
}

출력 결과

728x90

'프로그래밍 언어 > 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
COMMENT