본문 바로가기

Programming/JAVA

어노테이션(Annotation) - 런타임 시 어노테이션 정보 사용하기

java logo image

 

 

어노테이션의 경우 원래 아무런 동작을 하지 않는 단순한 표식 혹은 정보 값에 지나지 않을 수 있지만, 앞서 살펴본 리플렉션(Reflextion)을 통해서 정보를 획득하고, 일정한 처리를 진행할 수 있도록 지원하고 있습니다. 일단 클래스 / 필드 / 생성자 / 메서드에 적용된 어노테이션 정보를 획득해야 합니다. 

 

클래스에 적용된 어노테이션 정보 획득을 위해서는 java.lang.Class를 이용하면 되는데 필드 / 생성자 / 메서드에 적용된 어노테이션 정보 획득을 위해서는 Class의 메서드를 통해서 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 합니다. 

 

 

 

Class (Java Platform SE 8 )

Determines if the specified Class object represents a primitive type. There are nine predefined Class objects to represent the eight primitive types and void. These are created by the Java Virtual Machine, and have the same names as the primitive types tha

docs.oracle.com

 

 

Return Type Method Name(Parameter) Description
Field[] getFields() 필드 정보를 Field 배열로 리턴한다.
Constructor[] getConstructors() 생성자 정보를 Constructor 배열로 리턴한다.
Method[] getDeclaredMethods() 메서드 정보를 Method 배열로 리턴한다.

 

 

 

이제 위의 절차를 거쳐 획득한 Class, Field, Constructor, Method가 가진 아래의 메서드를 호출하게 되면 각각의 객체에 적용된 어노테이션 정보를 확인할 수 있게 됩니다. 

 

 

 

AnnotatedElement (Java Platform SE 7 )

Represents an annotated element of the program currently running in this VM. This interface allows annotations to be read reflectively. All annotations returned by methods in this interface are immutable and serializable. It is permissible for the caller t

docs.oracle.com

 

Return Type Method Name(Parameter)
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
지정한 어노테이션이 적용되었는지 여부를 리턴한다. Class에서 호출했을 때, 상위클래스에 적용된 경우에도 true를 리턴한다.
Annotation getAnnotation(Class<T> annotationClass)
지정한 어노테이션이 적용되어 있으면 해당 어노테이션을 리턴하고, 아닐 경우 null을 리턴한다. Class에서 호출했을 때, 상위클래스에 적용된 경우에도 적용된 어노테이션을 리턴한다.
Annotation[] getAnnotations()
적용된 모든 어노테이션을 리턴하며, 없을 경우 길이 0의 배열을 리턴한다. Class에서 호출했을 때, 상위클래스에 적용된 경우에도 적용된 어노테이션도 모두 포함해 리턴한다.
Annotation[] getDeclaredAnnotations()
직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때, 상위 클래스에 적용된 어노테이션은 포함되지 않는다.

 


 

예제를 확인해 보겠습니다. 아래 PrintAnnotation을 통해 각 메서드의 실행 내용을 "-" 구분선으로 분리해 출력하게 만들어 보겠습니다. 

 

/* PrintAnnotation.java*/

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
	String value() default "-";
	int number() default 15;
}

 

/* Service.java */

public class Service {
	@PrintAnnotation
	public void method1() {
		System.out.println("result 1");
	}
	
	// value 값 변경
	@PrintAnnotation("*")
	public void method2() {
		System.out.println("result 2");
	}
	
	// value, number 값 변경
	@PrintAnnotation(value = "#", number = 20)
	public void method3() {
		System.out.println("result 3");
	}
}

 

/* ExampleMain.java */

import java.lang.reflect.*;

public class ExampleMain {
	
	public static void main(String[] args) {
		// 리플렉션으로 Service 클래스의 메서드 획득
		Method[] declaredMethods = Service.class.getDeclaredMethods();
		
		// *Method 배열 객체를 하나씩 처리한다*
		for(Method method : declaredMethods) {
			// 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();
				
				// 마지막으로 해당 메서드를 실행해 준다.
				// invoke()는 Service 객체를 생성하고, 생성된 Service 객체 메서드를 호출한다.
				try {
					method.invoke(new Service());
				} catch (Exception e) {}
				
				System.out.println();
			}
		}
	}
}