언어/Java

어노테이션

O_oz 2023. 8. 24. 00:53
반응형

컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할지 알려주는 메타데이터 정보

사용 용도
컴파일러에게 코드 문법 에러를 체크하도록 정보 제공
소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보 제공
실행 시 (런타임 시) 특정 기능을 실행하도록 정보를 제공

ex) @Override : 메소드가 오버라이드 된 것임을 컴파일러에게 알려 컴파일러가 오버라이드 검사하도록 함 / 정확히 오버라이드 되지 않았다면 에러 발생 / 빌드 시 자동으로 XML 설정 파일을 생성 / 배포를 위해 JAR 압축 파일을 생성함 / 실행 시 클래스의 역할을 정의

 

어노테이션 타입 정의와 적용

public @interface 어노테이션 {
    타입 elementName() [default 값];
}

    - 어노테이션은 엘리먼트를 멤버로 가질 수 있으며, 각 엘리먼트는 타입과 이름으로 구성되고 디폴트 값을 가질 수 있음

    - 엘리먼트의 타입으로는 int, double 같은 기본 데이터  타입, String, 열거 타입, Class 타입, 이들의 배열 타입을 사용 가능

    - 엘리먼트 이름 뒤 메소드처럼 ()를 붙여야 함

public @interface AnnotationName {
    String value();
    int elementName() default 5;
}
@AnnotationName("값");
또는
@AnnotationName(value = "값", elementName = 3);

    - 디폴트 값이 없다면 반드시 값을 기술해야 하고 있으면 생략 가능

    - value  엘리먼트를 가진 어노테이션은 코드에 적용할 때 엘리먼트 이름을 입력하지 않아도 됨

 

어노테이션 적용 대상

    - java.lang.annotation,ElementType 열거 상수로 아래 표와 같이 정의 되어있음

ElementType 열거 상수 적용 대상
TYPE 클래스, 인터페이스, 열거 타입
ANNOTATION_TYPE 어노테이션
FIELD 필드
CONSTRUCTOR 생성자
METHOD 메소드
LOCAL_VARIABLE 로컬 변수
PACKAGE 패키지

    - @Target : 어노테이션이 적용될 대상을 지정할 때 사용 / @Target의 기본 엘리먼트인 value는 ElementType 배열을 값으로 가짐

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface AnnotationName {
}

    - 클래스, 필드, 메소드에서만 어노테이션 적용 가능

 

어노테이션 유지 정책

    - 어노테이션을 소스상에서만 유지할 건지, 컴파일된 클래스까지 유지할 건지, 런타임 시에도 유지할 건지 유지 범위를 지정해야 함

    - java.lang.annotation.RetentionPolicy 열거 상수로 아래 표와 같이 정의 되어 있음

RetentionPolicy 열거 상수 설명
SOURCE 소스상에서만 어노테이션 정보를 유지 / 소스 코드를 분석할 떄만 의미 있음
CLASS 바이트 코드 파일까지 어노테이션 정보 유지 / 리플렉션을 이용해 어노테이션 정보를 얻을 수 없음
RUNTIME 바이트 코드 파일까지 어노테이션 정보를 유지하며 리플렉션을 이용해 런타임 시 어노테이션 정보를 얻을 수 있음

    - 리플렉션 : 런타임 시에 클래스의 메타 정보를 얻는 기능 / 클래스가 가지고 있는 필드가 무엇인지, 어떤 생성자, 메소드를 가지고 있는지, 적용된 어노테이션이 무엇인지 / 어노테이션 유지 정책을 RUNTIME으로 설정해야 사용 가능

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName{
}

 

런타임 시 어노테이션 정보 사용하기

    - 리플렉션을 이용해 어노테이션의 적용 여부와 엘리먼트 값을 읽고 적절히 처리 가능

    - 어노테이션 정보를 얻기 위해선 java.lang.Class 이용

    - 필드, 생성자, 메소드에 적용된 어노테이션 정보를 얻기 위해선 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 함

리턴 타입 메소드명(매개 변수) 설명
Field[] getField() 필드 정보를 Field 배열로 리턴
Constructor[] getConstructor() 생성자 정보를 Constructor 배열로 리턴
Method[] getMethod() 메소드 정보를 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;
}
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");
    }
}
public class Example {
    public static void main(String[] args) {
        // Service 클래스로부터 메소드 정보를 얻음
        Method[] declaredMethods = Service.class.getDeclaredMethods();	// Service 클래스에 선언된 메소드 얻기 (리플렉션)
        
        // Method 객체를 하나씩 처리
        for (Method method : declaredMethods) {
            // PrintAnnotation이 적용되었는지 확인
            if (method.isAnnotationPresent(PrintAnnotation.class)) {
                // PrintAnnotation 객체 얻기
                PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
                
                // 메소드 이름 출력
                System.out.print("[" + method.getName() + "]");
                
                // 구분선 출력
                for (int i = 0; i < printAnnotation.number(); i++) {
                    System.out.print(printAnnoation.value());
                }
                System.out.println();
                
                try {
                    // 메소드 호출
                    method.invoke(new Service());
                } catch (Exception e) {}
                System.out.println();
            }
        }
    }
}
출력 결과 :
[method1]
---------------
실행 내용1

[method2]
***************
실행 내용2

[method3]
####################
실행 내용3
반응형

'언어 > Java' 카테고리의 다른 글

메소드 재정의 (오버라이딩)  (0) 2023.08.24
상속  (0) 2023.08.24
Getter와 Setter  (0) 2023.08.23
접근 제한자  (0) 2023.08.23
클래스  (2) 2023.08.23