ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • java Annotation 이해하기
    Language/Java 2023. 9. 25. 22:17
    728x90
    반응형

    - 목차

     

    소개.

    java Annotation 은 일종의 마커입니다.
    Annotation 그 자체만으로 특별한 기능을 수행하는 것은 아닙니다.
    Annotation 이 붙은 class, method, field 들이 컴파일러 또는 런타임 환경을 거치면서 특별한 기능을 수행하게 됩니다.
    컴파일러가 컴파일을 하던 도중에 특정 Annotation 을 발견한다면,
    약속된 Annotation 의 처리방식에 따라서 어노테이션이 붙은 class, method, field 를 컴파일합니다.
    그리고 Spring 과 같은 런타임 환경에 어플리케이션을 구동하는 과정에서
    Annotation이 붙은 class, method, field 들을 발견하면 이에 상응하는 처리를 수행합니다.
     
    예를 들어보겠습니다.
     

    @Deprecated.

    우리가 흔히 하는 @Deprecated Annotation 이 있죠?
    @Deprecated Annotation 은 대표적인 Source Retention Annotation 입니다.
    이는 컴파일러에 의해서 컴파일되는 과정에서 처리되는 Annotation 입니다.
     
    컴파일러가 java 파일을 bytecode 로 컴파일을 시도하죠.
    컴파일하는 과정에서 Deprecated 로 선언된 Method 를 사용하는 함수를 발견하게 된다면,
    Deprecated 되었다는 경고문을 출력합니다.
     
    즉, 컴파일러는 컴파일 과정에서 @Deprecated Annotation 이 붙은 함수가
    사용되는 코드를 발견하게 되면 경고문을 출력하도록 설정되어 있습니다.
     

    @Override.

    @Override Annotation 는 대표적인 Compile-Time Annotation 입니다.
    컴파일 과정에서 사용되는 Annotation 이구요.
    @Override Annotation 이 적용된 Method 를 자식 클래스에서 Override 하지 않는다면,
    해당 컴파일은 실패하게 됩니다.
     

    @Test.

    @Test Annotation 은 Runtime Annotation 중의 하나입니다.

    Runtime Annotation 은 컴파일러가 아닌 실행 환경에 의해서 처리되는 Annotation 인데요.
    JUnit 이라는 Test Framework 에 의해서 처리되는 어노테이션입니다.
    java 의 JUnit 테스트 환경에서 첫 단계로 테스트 함수들을 찾는 과정이 있습니다.
    이를 Test Discovery 라고 부르는데요.
    JUnit Test Engine 이 @Test 라고 어노테이션이 붙은 모든 함수를 스캔하게 됩니다.
    ( 이 과정에서 Java Reflection 이 사용됩니다. )
     
    즉, JUnit Test Engine 은
    모든 classpath 의
    모든 class 의
    모든 @Test Annotation 가 붙은 method 들을
    조회하는 과정을 필수적으로 거칩니다.
    이를 Test Method Scan 이라고 합니다.
     
    @Test Annotation 는 하나의 마커로써 사용되는 대표적인 케이스입니다.
     

    @Bean.

    스프링을 사용하다보면 수 많은 @Bean Annotation 를 볼 수 있죠.
    이 또한 스프링이라는 런타임 환경에 Bean 을 생성하기 위해서 @Bean Annotation 이 붙은 모든 클래스를 찾습니다.
    이 또한 @Test 처럼 ComponentScan 과정을 거치죠.
     
     

    Retention Policy.

    Annotation 은 Retention 이라는 중요한 기준이 있습니다.
    Retention 이 해당 Annotation 이 유효함을 가지는 시간 정보인데요.
    Source, Compile, Runtime 두가지로 나뉩니다.

    Source.

    Retention 이 Source 인 Annotation 은 처리 결과가 Bytecode 에 남지 않습니다.
    Annotation 의 수명이 Source Code 인 상태에서만 유지되기 때문에 Source 라고 표현됩니다.

    대표적인 Source Retention 은 Deprecated 말고도 Lombok 으로 제공되는 Annotation 들이 있습니다.
    @getter, @setter 이 그 예이죠.
    Lombok 을 사용하기 위해서는 필수적으로 Lombok annotation processer 가 컴파일 과정에 추가되어야 합니다.

    dependencies {
      annotationProcessor 'org.projectlombok:lombok:1.18.22'
    }
    


    이것은 컴파일 과정의 앞단인 Annotation Processing 단계에서 Lombok Annotation Processor 가 동작함을 뜻합니다.

    만약 AnnotationProcessor 가 @getter 또는 @setter 인 field 를 발견한다면,
    Lombok Annotation processor 는 해당 field의 set method 와 get method 를 만들어줍니다.
    그리고 나서 컴파일이 되는거죠.

    Source Retention Annotation 의 특징은
    1. 그 수명이 Source Code 를 넘어가지 않습니다.
    2. bytecode 에 Source Retention Annotation 의 흔적이 남지 않습니다.
    3. Deprecated 같은 built-in annotation 은 컴파일러에 의해서 사전에 정의된 방식으로 처리됩니다.
    4. Lombok 과 같은 custom annotation 은 이것들을 처리하기 위한 별도의 Annotation Processor 가 필요합니다. (ex. Lombok Annotation Processor)

     
     

    Compile.

    Compile Retention Annotation 은 컴파일 과정에서 사용되는 Annotation 입니다.
    Source Retention Annotation 이 Source Code 레벨에서만 존재하듯이, Compile Retention Annotation 은 bytecode 단계까지만 어노테이션이
    유지되고 런타임에선 유지되지 않습니다.
    하지만 최근 스펙의 java 에선 Compile Retention Annotation 이 컴파일 과정에서 Runtime Retention 으로 치환되어 런타임 환경에서도 확인할 수 있다고 합니다.
    즉, java Reflection 을 통해 Compile Retention Annotation 도 확인이 가능하죠.

     <java spec>
    대략 Compile Retention Annotation 이 Runtime Retention 으로 변경되어 컴파일된다는 의미입니다.

    The Java compiler can generate a runtime retention annotation for a compile retention annotation. 
    This is done by generating a compile retention annotation called @Retention(RetentionPolicy.RUNTIME) 
    when it compiles a class that is annotated with a compile retention annotation.

     
    그래서 사실상 기술적으로는 Compile 과 Runtime 의 어노테이션이 큰 차이는 없습니다.
    하지만 기존의 컨벤션에 따라 그리고 의미적인 관점에서 따라 다르게 사용되어야 할 뿐이죠.
    디저트를 식후에 먹든 식전에 먹든 별 상관은 없지만, 보통 식후에 먹죠?
    특히 유럽에선 디저트를 아무때나 먹으면 굉장히 화를 낸다고 합니다.

    Compiler 는 Compile Retention Annotation 을 확인하고, 이에 상응하는 처리를 진행할 수 있습니다.
      

    Runtime.

    런타임 어노테이션은 굉장히 많은 종류가 있습니다.
    왜냐하면 런타임 환경이라는게
    Spring, JUnit, Spark, Flink 등등 여러 가지가 존재합니다.
    Runtime Retention Annotation 을 사용하기 위해서는 두가지가 필수적으로 필요합니다.

     

    1. Custom Runtime Annotation Processor

    2. 약속된 Runtime Annotation

     

    Spring, JUnit 처럼 자체적인 Annotation Processor 가 존재해야하구요.

    Application 실행 단계에서 Annotation Processing 단계가 실행되어야합니다.

    당연히 Annotation Processor 가 처리할 수 있는 약속된 Runtime Annotation 도 필요합니다.

     

    가장 대표적인 Runtime Retention Annotation 은 @Test 와 @Bean 입니다.

    관련된 글이 있어서 첨부드립니다.

     

    https://westlife0615.tistory.com/4

     

    JUnit Test Discovery 알아보기

    - 목차 소개. JUnit Test Framework 에는 Test Runner 라는 코어 컴포넌트가 존재합니다. Test Runner 가 수행하는 여러 역할들 중에서 Test Discovery 에 대해서 살펴보려고 합니다. Test Discovery 는 선언된 모든 테

    westlife0615.tistory.com

    https://westlife0615.tistory.com/7

     

    Spring Bean 알아보기

    - 목차 Bean 이란? Bean 은 스프링에서 관리하는 Java Object 입니다. 일반적인 스프링 웹 환경에서 Controller, Service, DataSource, ThreadPool 등이 Bean 으로써 사용됩니다. 직접 생성하는 Java Object 와 Bean 의 차

    westlife0615.tistory.com

     

     

    Runtime 에서 Annotation 확인하는 법.

     

    아래 코드는 Field 와 Method 어노테이션을 적용하는 코드입니다.

     

    package org.example;
    
    import java.lang.annotation.*;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class AnnotationTest {
    
      @TestRuntimeAnnotation1()
      String field1;
    
      @TestRuntimeAnnotation2()
      String getField1 () {
        return field1;
      }
    
      public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        Field field1 = AnnotationTest.class.getDeclaredField("field1");
        field1.setAccessible(true);
        Annotation[] fieldAnnotations = field1.getAnnotations();
        System.out.printf("field annotation is %s \n", fieldAnnotations[0].annotationType().getName());
    
        Method getField1 = AnnotationTest.class.getDeclaredMethod("getField1");
        getField1.setAccessible(true);
        Annotation[] methodAnnotations = getField1.getAnnotations();
        System.out.printf("method annotation is %s \n", methodAnnotations[0].annotationType().getName());
        
      }
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface TestRuntimeAnnotation1 {
    }
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface TestRuntimeAnnotation2 {
    }

     

    <실행 결과>

    field annotation is org.example.TestRuntimeAnnotation1 
    method annotation is org.example.TestRuntimeAnnotation2

     

     

    이러한 방식으로 모든 Runtime Annotation Processor 들은 classpath 의 모든 클래스들을 스캔하고 Annotation 처리를 수행합니다.

    Field, Method, Constructor, Class 들을 조회하게 되죠.

     

     

     

    마무리하며.

    java 의 Annotation 은 그 자체만으로 특별한 기능을 가지는 것은 아닙니다.

    java compiler, Custom Annotation Processor 등을 통해서 비로소 의미를 가지게 됩니다.

    Lombok 과 같은 Source Level Annotation 들은 새로운 코드를 생성하거나,

    Swagger 같은 기능으로 Documentation 을 생성할 수도 있죠.

    그리고 Spring 이나 JUnit 같은 경우도 Runtime Annotation 들을 찾아서

    Bean 을 생성하거나 Test 케이스들을 수집할 수 있습니다.

     

    지금까지 java Anontation 의 의미와 쓰임에 대해서 알아보았습니다.

    반응형

    'Language > Java' 카테고리의 다른 글

    [Java NIO] NIO 는 어떻게 동작할까 ? (DMA, Kernel Space)  (0) 2024.01.31
    Jackson 으로 JSON 다루기  (0) 2024.01.22
    Java off-heap 메모리 알아보기  (0) 2023.12.22
    Java Thread Pool 알아보기  (0) 2023.09.30
    Java Future 알아보기  (0) 2023.09.30
Designed by Tistory.