[JAVA] 어노테이션
나는 스프링 프레임워크로 웹 개발에 입문했다.
스프링은 개발 시에 많은 어노테이션을 사용하여 편하게 개발할 수 있도록 도와준다.
그래서 이번엔 평소에 궁금했던 어노테이션에 관하여 알아보고 커스텀 어노테이션을 만들어 보았다.
우선 어노테이션은 기본적으로 세가지 타입이 존재한다.
Marker Annotation : 표시만 해 놓는 어노테이션
Sigle Value Annotation : 하나의 입력 값만 존재하는 어노테이션
Multi Value Annotation : 여러 입력 값이 존재하는 어노테이션
다음으로 커스텀 어노테이션은 어노테이션을 위한 어노테이션인 메타 어노테이션(Meta Annotation)을 사용한다.
@Retention : 컴파일러가 어노테이션을 다루는 방법을 기술, 어느 시점까지 영향을 미치는지를 결정
RetentionPolicy.SOURCE : 컴파일 전까지만 유효
RetentionPolicy.CLASS : 컴파일러가 클래스를 참조할 때까지 유효
RetentionPolicy.RUNTIME : 컴파일 이후 런타임 시기에도 JVM에 의해 참조가 가능(리플렉션)
@Target : 어노테이션 적용할 위치 선택
ElementType.PACKAGE : 패키지 선언
ElementType.TYPE : 타입 선언
ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언
ElementType.CONSTRUCTOR : 생성자 선언
ElementType.FIELD : 멤버 변수 선언
ElementType.LOCAL_VARIABLE : 지역 변수 선언
ElementType.METHOD : 메서드 선언
ElementType.PARAMETER : 전달인자 선언
ElementType.TYPE_PARAMETER : 전달인자 타입 선언
ElementType.TYPE_USE : 타입 선언
@Documented : 해당 어노테이션을 Javadoc에 포함시킴
@Inherited : 어노테이션의 상속을 가능하게 함
@Repeatable : Java8 부터 지원하며, 연속적으로 어노테이션을 선언할 수 있게 함
어노테이션은 추가적인 정보만 제공해준다. 이 정보를 이용하기 위해 자바 리플렉션을 사용하여 개발자가 표시한 정보에 접근하고 해당 정보를 활용하도록 프로그램을 작성하는 것이다.
아래는 자바 리플렉션에 관해 참조할 만한 글이다
https://steady-coding.tistory.com/609
[Java] Reflection 개념 및 사용 방법
java-study에서 스터디를 진행하고 있습니다. Reflection이란? 리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메
steady-coding.tistory.com
추후 리플렉션에 대한 글도 포스팅 할 예정이다.
커스텀 어노테이션을 생성하여 이를 활용해 보았다.
과일에 관한 정보를 입력하도록 커스텀 어노테이션을 구성하고 이를 실행한다.
1. @FruitColor 어노테이션
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
enum Color{BLUE, RED, GREEN}
Color fruitColor() default Color.GREEN;
}
클래스의 필드 값에 적용하도록 @Target 을 ElementType.FIELD
런타임 시기에도 값을 참조할 수 있도록 @Retention 을 RetentionPolicy.RUNTIME (리플렉션을 사용하려면 런타임 시기까지 어노테이션이 살아있도록 해야한다.)
로 구성하였다.
2. @FruitName 어노테이션
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "모르는 과일";
}
1번과 같이 동일하게 적용되도록 하고 값을 value 에 대한 값을 갖도록 설정해 주었다.
3. @FruitProvider 어노테이션
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
int id() default -1;
String name() default "";
String address() default "";
}
1, 2번과 동일하게 적용되도록 하고 multi value 를 갖도록 만들었다.
4. FruitInfoUtil
어노테이션에 관한 정보를 사용할 수 있도록 Util을 만들어 주었다.
import java.lang.reflect.Field;
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName = " 과일 이름 : ";
String strFruitColor = " 과일 색 : ";
String strFruitProvider = " 과일 파는 곳 : ";
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
if (field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = field.getAnnotation(FruitName.class);
strFruitName = strFruitName + fruitName.value();
System.out.println(strFruitName);
}else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
strFruitColor = strFruitColor + fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
strFruitProvider = " 과일 파는 곳의 ID: " + fruitProvider.id()
+ " 지점 이름 : " + fruitProvider.name()
+ " 지점 주소 : " + fruitProvider.address();
System.out.println(strFruitProvider);
}
}
}
}
리플렉션 API 인 class.getDeclaredFields()를 사용하여, 어노테이션이 사용된 클래스들을 전부 가져와
반복하며 어노테이션이 존재하는 필드를 처리하는 것이다.
5. Apple
정보를 입력할 클래스를 생성하고 어노테이션으로 각 필드에 표시해준다.
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor = FruitColor.Color.RED)
private String appleColor;
@FruitProvider(id = 1, name = "HomePlus", address = "Seoul")
private String appleProvider;
public String getAppleName() {
return appleName;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleProvider() {
return appleProvider;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
}
6. FruitRun
public class FruitRun {
public static void main(String[] args){
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
main 메소드에서 Util의 메소드를 사용해주면
과일 이름 : Apple
과일 색 : RED
과일 파는 곳의 ID: 1 지점 이름 : HomePlus 지점 주소 : Seoul
이와같이 사전에 클래스 파일에 설정한 값대로 결과가 나타나게 된다.
스프링에서 주로 Bean을 등록할 때 어노테이션을 많이 사용한다.
이는 @Component를 적용한 클래스들을 스프링이 시작된 후 ClassPathBeanDefinitionScanner.java 파일의 doScan() 메서드에서 스캔하며 IoC컨테이너에 등록하는 과정을 거쳐 적절하게 사용될 수 있게 한다.