Custom Annotation 만들기



Gson 이라는 클래스를 쓸때 @SerializedName("fieldName") 라는 쓰는 경우가 있고

이러한 Annotation의 원리가 궁금했다.


1. interface앞에 @를 붙여서 Annotation Interface를 선언한다.

2. Annotation을 구현한다. (Annotation 사용자의 경우)

3. Reflection을 이용하여 객체나 class에서 선언된 필드나 메소드를 찾고 적절한 동작을 구현한다.

의 순서로 접근하면 된다.



1. interface앞에 @를 붙여서 Annotation Interface를 선언한다.

@Inherited

@Documented

@Retention(RetentionPolicy.RUNTIME) // 컴파일 이후에도 JVM에 의해서 참조가 가능합니다.

//@Retention(RetentionPolicy.CLASS) // 컴파일러가 클래스를 참조할 때까지 유효합니다.

//@Retention(RetentionPolicy.SOURCE) // 어노테이션 정보는 컴파일 이후 없어집니다.

@Target({

        ElementType.PACKAGE, // 패키지 선언시

        ElementType.TYPE, // 타입 선언시

        ElementType.CONSTRUCTOR, // 생성자 선언시

        ElementType.FIELD, // 멤버 변수 선언시

        ElementType.METHOD, // 메소드 선언시

        ElementType.ANNOTATION_TYPE, // 어노테이션 타입 선언시

        ElementType.LOCAL_VARIABLE, // 지역 변수 선언시

        ElementType.PARAMETER, // 매개 변수 선언시

        ElementType.TYPE_PARAMETER, // 매개 변수 타입 선언시

        ElementType.TYPE_USE // 타입 사용시

})


public @interface CustomAnnotation {

    

    String name() default "default";

    String value() default "default";

}

- name(), value()가 attribute명이자 getter가 된다.

- default는 필드값을 안채우거나 하나만 쓸때 채워지는 값이다.

- interface위의 필드(Target, Retention 등)는 주석을 보거나 실생활에 쓰이고 있는 case를 참고하는것이 좋겠다.


1-1. 사용사례

 @Override

 package java.lang;

 ...

 @Target(ElementType.METHOD)

 @Retention(RetentionPolicy.SOURCE)

 public @interface Override {

 }

 @SerializedName

 package com.google.gson.annotations;

 ...

 @Retention(RetentionPolicy.RUNTIME)

 @Target({ElementType.FIELD})

 public @interface SerializedName {

     String value();

 }

@Test 

 package org.junit;

 ...

 @Retention(RetentionPolicy.RUNTIME)

 @Target({ElementType.METHOD})

 public @interface Test {

   Class<? extends Throwable> expected() default Test.None.class;

   long timeout() default 0L;

   public static class None extends Throwable {

        private static final long serialVersionUID = 1L;

        private None() {

        }

    }

}

- Override 주석을 보면 그 유명한 Effective Java의 저자 조슈아 블로치(Joshua Bloch)이고 Java 1.5부터 반영된것으로 보인다!!



2. Annotation을 구현한다. (Annotation 사용자의 경우)

public class CustomAnnotationImpl {

    @CustomAnnotation("aaa")

    private String name;


    @CustomAnnotation(value="ccc", name="bbb")

    private String value;


    CustomAnnotationImpl(String name, String value) {

        this.name = name;

        this.value = value;

    }


    public String getName() {

        return name;

    }


    public void setName(String name) {

        this.name = name;

    }


    public String getValue() {

        return value;

    }


    public void setValue(String value) {

        this.value = value;

    }


    @CustomAnnotation("print")

    public void printNameAndValue() {

        System.out.println("name=" + name + ", value=" + value);

    }

}

- 1에서처럼 매개변수가 여러개로 선언했으나 @CustomAnnotation("aaa")인 경우는 매개변수 선언 순서상의 마지막에 할당되는 것 같다.



3. Reflection을 이용하여 객체나 class에서 선언된 필드나 메소드를 찾고 적절한 동작을 구현한다.

 Field 찾기

         for (Field field : fieldArray) {

            String fieldName = field.getName();

            CustomAnnotation testAnnotation = field.getAnnotation(CustomAnnotation.class);

            Annotation[] annotationArray = field.getDeclaredAnnotations();

            boolean isAnnotationPresent = field.isAnnotationPresent(CustomAnnotation.class);


            if (testAnnotation != null) {

                field.setAccessible(true);

                

                if (obj != null) {

                    String fieldValue = (String)field.get(obj);

                    field.set(obj, "33");

                    fieldValue = (String)field.get(obj);

                }

            }

        }

 Method 찾기

         for (Method method : methodArray) {

            String methodName = method.getName();

            CustomAnnotation testAnnotation = method.getAnnotation(CustomAnnotation.class);

            Annotation[] annotationArray = method.getDeclaredAnnotations();

            boolean isAnnotationPresent = method.isAnnotationPresent(CustomAnnotation.class);

            

            if (testAnnotation != null) {                

                if (obj != null) {

                    method.invoke(obj);

                }

            }

        }

- 하지만 Reflection은 성능이 좋지 않다는 치명적인 단점이 있다. 매개변수의 타입체킹도 런타임에 되서 위험할 수도 있고...

  잘 쓰면 괜찮다고는 하지만 그만큼 적절한가를 따져야 한다.



위 내용에 대한 코드는 github에서 확인하실 수 있습니다.

https://github.com/ndukwon/Duk_Java_library/tree/master/src/com/duk/lab/java/annotation



Reference:

- http://jdm.kr/blog/216

- https://medium.com/@ggikko/java-%EC%BB%A4%EC%8A%A4%ED%85%80-annotation-436253f395ad

- https://www.javatpoint.com/custom-annotation






'Program Languages > Java' 카테고리의 다른 글

Loop 문법과 속도  (0) 2017.06.26

+ Recent posts