스프링 프로젝트를 생성하면 @Component와 같은 키워드가 달려있는걸 볼 수 있다. 이걸 자바에선 어노테이션이라 부르는데, 클래스, 메서드, 변수에 달 수 있다고 한다.
어노테이션은 무엇인가?
어노테이션이란 프로그램에 추가적인 정보를 제공해주는 메타데이터라고 볼 수 있다. 여기서 메타데이터란 어플리케이션이 처리해야 할 데이터가 아니라 컴파일 과정과 런타임에서 코드를 어떻게 컴파일하고 처리할 것인지에 대한 정보를 말한다.
- 이 메타데이터를 잘 이용하면 비즈니스 로직과 분리하여 대상의 유효성 검증, 값 주입, 역할 부여(기능 주입) 등을 수행할 수 있어 코드를 좀 더 깔끔하게 작성할 수 있게 된다.
- 어노테이션은 옵션에 따라 컴파일 전까지만 유효하도록 처리될 수 있고, 컴파일 시기에 처리될 수도 있고, 런타임 시기에 처리될 수 있다.
- Java의 리플렉션(실행 중인 자바 클래스의 정보를 가져오는 기능)을 사용하여 런타임에 어노테이션의 정보를 바탕으로 다양한 기능을 수행할 수 있기 때문에, AOP(관점지향 프로그래밍)을 구성하는데 도움을 줄 수 있다.
어노테이션은 빌트인 어노테이션과 메타 어노테이션으로 나뉘는데, 빌트인은 말 그대로 자바에서 선언해놓은 어노테이션들이고, 메타 어노테이션이 우리가 수정할 수 있는 영역이다.
어노테이션은 어떻게 만드는가?
@Target({ElementType.[적용대상]})
@Retention(RetentionPolicy.[정보유지되는 대상])
public @interface [어노테이션명]{
public 타입 elementName() [default 값]
...
}
어노테이션의 필드 타입은 enum, String이나 기본 자료형, 기본 자료형의 배열만 사용할 수 있다.
메타 어노테이션의 종류는 다음과 같다.
@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 부터 지원하며, 연속적으로 어노테이션을 선언할 수 있게 함
어노테이션은 어떻게 동작하는가?
어노테이션은 특별한 종류의 인터페이스로 취급한다. 일반적인 인터페이스와 구분하기 위해 @기호와 interface 키워드를 붙여 선언한다.
어노테이션은 그 자체로는 아무것도 해주는게 없지만, 메타데이터이기 때문에 이 정보를 활용할 수 있는 다른 매개체가 필요하다.
정보를 이용하기 위해선 어노테이션에 대한 접근이 필요하고, 클래스 메소드와 필드에 대한 정보를 얻고 싶으면, 리플렉션을 이용하여 얻어야 한다.
1. 내 어노테이션 만들기
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String nickname() default "no nickname";
}
@MyAnnotation(nickname = "lion")
public class MyId {
String name;
int age;
public MyId(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class AnnotationStudy {
public static void main(String[] args) {
MyId myId = new MyId("John", 10);
AnnotationStudy as = new AnnotationStudy();
as.printNickname(myId);
}
public void printNickname(MyId myId) {
//MyId 클래스의 인스턴스들 중에서 Annotation이 붙은 것들을 모두 가져옴
Annotation[] annotations = MyId.class.getDeclaredAnnotations();
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation m = (MyAnnotation) annotation;
System.out.println(myId.getName() + "'s nickname is " + m.nickname() + ".");
}
}
}
}
/*
Output:
John's nickname is lion
*/
'Java' 카테고리의 다른 글
Java의 static method는 왜 오버라이딩 되지 않는가 (0) | 2022.01.23 |
---|---|
오늘의 질문 : Java의 Enum은 왜 생성자가 있는가? (0) | 2021.08.17 |
모던 자바인액션 1장 (0) | 2021.06.26 |
Java Collection (0) | 2021.04.01 |
자바 빌더(Builder) 패턴 + lombok (0) | 2021.03.25 |