티스토리 뷰
예전에 커스텀 어노테이션을 만드는 방법에 대해 글을 정리했었다.
https://pamyferret.tistory.com/47
그 때 만들었던 커스텀 어노테이션은 단순히 속성 값을 하나만 받는 어노테이션이었다.
하지만 만일 하나의 속성에 대해 값을 여러 개 받아야하는 경우 어떻게 해야할까?
그럴 때는 해당 속성의 타입을 배열로 지정하면 간단하게 끝날 일이다. 참고로 List<T>와 같은 컬렉션은 어노테이션의 멤버로 설정이 불가하니 일반 배열을 사용해야한다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Animal {
String[] value() default {};
}
// 어노테이션 멤버 이름이 value일 경우 value라는 것을 명시해주지 않아도 된다.
@Animal({"cat", "dog"})
public class Test {
}
보통 이렇게 배열로 어노테이션 멤버 타입을 정의해 속성 값을 여러 개 받곤 하는데 이 방법 말고 @Repeatable 어노테이션을 사용해서 받는 방법도 있어 정리해본다.
@Repeatable 사용 방법
우선 @Repeatable을 간단하게 설명해보면 동일한 어노테이션을 같은 곳에 중복해서 사용 가능하게 만들어주는 어노테이션으로 java 8부터 사용이 가능하다.
단, @Repeatable 어노테이션 사용만으로 끝나는게 아니라 몇 가지 처리가 필요하다.
@Repeatable 어노테이션의 안을 까보면 아래와 같이 value 값을 받도록 되어 있다. 여기서 value는 어노테이션 타입이어야 한다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
value에 들어갈 어노테이션 타입은 중복해서 사용할 어노테이션을 묶을 일종의 컨테이너 어노테이션을 의미한다.
컨테이너 어노테이션은 하나로 묶고자 하는 어노테이션 배열을 멤버로 갖는다. 위의 Animal 어노테이션을 묶는 컨테이너 어노테이션을 만들면 아래와 같다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnimalContainer {
Animal[] value();
}
그리고 @Repeatable 어노테이션은 묶고자 하는 어노테이션 자체에 작성해주면 된다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(AnimalContainer.class)
public @interface Animal {
String value() default "";
}
이렇게 @Repeatable을 정의하고나면 위에서 했던 것처럼 value에 여러 값을 넣고 싶을 때 배열이 아닌 어노테이션을 여러 번 쓰는 것으로 해결할 수 있다.
@Animal("cat")
@Animal("dog")
public class Test {
...
}
@Repeatable 어노테이션을 사용하는 이유
기존에 배열 방식이 있는데 왜 @Repeatable 어노테이션을 사용할까? 별도로 컨테이너 어노테이션을 정의해야해서 복잡해보이긴 하지만 어노테이션에 여러 값을 받아야하는 속성에 따라 하나 하나 명시하는 것이 가독성면에서 @Repeatable 어노테이션을 이용하는 것이 더 좋을 수 있다.
또한 아래와 같이 어노테이션의 여러 멤버들의 조합으로 어노테이션이 다르게 동작해야한다면 배열로 멤버를 여러 개 받아서 조합을 하기보다는 @Repeatable을 통해 명시적으로 조합을 다르게 해 어노테이션을 중복 선언하게 하는 것이 좋다.
@Test(value1="aaa", value2=3, value4="test")
@Test(value1="bbb", value2=4, value4="blue")
...
@Repeatable 사용시 주의할 점
그렇다면 @Repeatable을 사용해 어노테이션을 중복 선언하게 했을 경우 주의해야할 점은 무엇일까?
우선 어노테이션과 컨테이너 어노테이션이 각기 다른 클래스라는 것에 주목해야한다. 이에 따라 어노테이션을 중복 선언한 것에 대해서는 컨테이너 어노테이션으로 값이 묶이지만 어노테이셔을 중복 선언하지 않을 것은 컨테이너 어노테이션으로 묶이지 않는다.
// 1. AnimalContainer로 묶인다.
@Animal("cat")
@Animal("dog")
@Component
public class Test1 {
}
// 2. Animal로 묶이지 않는다.
@Animal("cat")
@Component
public class Test2 {
}
위에 대해 아래와 같이 각각 Animal, AnimalContainer를 보유하고 있는지 확인해보면 결과가 다르다.
@Test
public void repeatableTest() {
Test1 test1 = new Test1();
Test2 test2 = new Test2();
AnimalContainer animalContainer1 = test1.getClass().getAnnotation(AnimalContainer.class);
Animal[] animals1 = test1.getClass().getAnnotationsByType(Animal.class);
AnimalContainer animalContainer2 = test2.getClass().getAnnotation(AnimalContainer.class);
Animal[] animals2 = test2.getClass().getAnnotationsByType(Animal.class);
System.out.println(animalContainer1 == null); // false
System.out.println(animals1.length); // 2
System.out.println(animalContainer2 == null); // true
System.out.println(animals2.length); // 1
}
따라서 이 점을 인지하고 @Repeatable로 정의한 어노테이션을 사용하는 것이 좋다.
✋ @Repeatable
https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Repeatable.html
'Java' 카테고리의 다른 글
Mockito로 static method mock 하기 (0) | 2022.03.17 |
---|---|
빌더 패턴(Builder pattern)을 써야하는 이유, @Builder (2) | 2022.03.02 |
Build Gradle vs IntelliJ IDEA (2) | 2022.02.10 |
자바의 function interface (0) | 2022.02.04 |
직렬화 프록시 패턴이란 (0) | 2022.01.28 |
- Total
- Today
- Yesterday
- enum
- 캐시
- Spring
- annotation
- 공간데이터
- mockito
- DB
- 메시지큐
- HttpClient
- k8s
- 캐싱
- Java
- Intellij
- 스프링부트
- 이클립스
- postgres
- 역직렬화
- springboot
- 어노테이션
- 데이터베이스
- JPA
- rabbitmq
- Caching
- DATABASE
- PostgreSQL
- MAC
- eclipse
- ssh
- 자바
- cache
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |