반응형
@Retention
public @interface MyAnnotation {
}
기본적으로 애노테이션은 주석과 마찬가지로 취급한다.
- .class 까지는 애노테이션 정보가 남지만 클래스 로딩 후에 메모리에는 애노테이션 정보가 남지 않는다.
- 런타임 까지도 애노테이션 정보를 유지하고 싶다면 @Retention(RetentionPolicy.RUNTIME) 설정을 애노테이션 위에 추가해주어야 한다.
- 기본값은 @Retention(RetentionPolicy.CLASS) 이다.
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
.class 파일에 들어있는 애노테이션 정보는 다음 명령을 통해 확인할 수 있다.
javap -c -v .class파일의 경로
- F:\all_workspace\the_java_workspace\refactoring-example\target\classes\me\devhist ory\Book.class
- @Retention(RetentionPolicy.CLASS) 설정 애노테이션은 런타임에 볼 수 없고 아래와 같이 표시된다.
. . (생략) . SourceFile: "Book.java" RuntimeInvisibleAnnotations: 0: #58() me.devhistory.MyAnnotation
- @Retention(RetentionPolicy.RUNTIME) 설정을 추가하면 런타임에 애노테이션 정보를 볼 수 있으며 아래와 같이 표시된다.
. . (생략) . SourceFile: "Book.java" RuntimeVisibleAnnotations: 0: #58() me.devhistory.MyAnnotation
- 런타임에서 볼 수 없는 애노테이션은 리플렉션 API를 사용해도 조회가 되지않는다.
- 메모리에 애노테이션 정보가 없기 때문이다.
Arrays.stream(Book.class.getAnnotations()).forEach(System.out::println); //조회 정보 없음
- 런타임에서 볼 수 있는 애노테이션은 리플렉션 API를 이용해서 값을 조회할 수 있다.
Arrays.stream(Book.class.getAnnotations()).forEach(System.out::println); //@me.devhistory.MyAnnotation()
@Target
@Target을 설정하게 되면 애노테이션을 사용할 수 있는 위치를 제한할 수 있다.
@Target({ElementType.TYPE, ElementType.FIELD})
- TYPE, FIELD 이외의 영역에서 해당 애노테이션을 사용하게되면 컴파일 에러 발생.
@MyAnnotation
public class Book {
@MyAnnotation private static String B= "BOOK";
private static final String C= "BOOK";
private String a = "a";
public String d = "d";
protected String e = "e";
public Book() {
}
// @MyAnnotation 컴파일 에러 발생
public Book(String a, String d, String e) {
this.a = a;
this.d = d;
this.e = e;
}
private void f(){
System.out.println("F");
}
public void g(){
System.out.println("g");
}
public int h(){
return 100;
}
}
애노테이션 엘리먼트로 올 수 있는 타입은 다음과 같다.
- 기본형(primitive type)
- 문자열(String)
- 클래스
- 열거 타입(enum)
- 애노테이션
- 위의 타입으로 구성된 배열
애노테이션에 엘리먼트가 있고 기본값이 설정되어 있다면, 애노테이션 사용 시 꼭 값을 넘겨줄 필요는 없다. 인자를 전달받지않으면 기본값으로 설정된다.
@MyAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
String name() default "devhistory";
int number() default 100;
}
그러나 애노테이션에서 값을 선언해서 사용하는데 기본값이 설정되지 않은 경우라면 해당 애노테이션(MyAnnotation)을 사용할 때 값을 반드시 넘겨주어야 한다.
@MyAnnotation(name = "devhistory", number = 100)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
String name();
int number();
}
만약 애노테이션에 선언된 값 중 이름이 value() 인 것은 애노테이션 사용 시 이름을 명시적으로 적어주지 않아도 된다.
@MyAnnotation(value = "devhistory")
@MyAnnotation("devhistory")
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
String value() default "devhistory";
int number() default 100;
}
그러나 여러 개의 속성을 설정하는 경우에는 이름이 value() 라도 명시적으로 이름을 작성해야 한다.
@MyAnnotation(value = "devhistory", number = 100)
@Inherited
부모 클래스에서 사용한 애노테이션 정보를 자식에게도 상속할 수 있도록 할 수 있다.
MyBook 클래스는 @Target 에서 보았던 Book 클래스를 상속하고 있다.
@MyAnnotation("devhistory2")
public class MyBook extends Book implements MyInterface{
}
그리고 MyAnnotation 애노테이션을 수정하여 상속 되도록 하였다.
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Inherited
public @interface MyAnnotation {
String value() default "devhistory";
int number() default 100;
}
- 클래스에 정의된 애노테이션(상속받은 애노테이션 포함) 조회
getAnnotations()
- 부모에서 상속받은 애노테이션과 현재 클래스에 정의된 애노테이션이 같다면 현재 클래스의 애노테이션만 표시된다.
Arrays.stream(MyBook.class.getAnnotations()).forEach(System.out::println); //@me.devhistory.MyAnnotation(value="devhistory2", number=100)
- 부모에서 상속받은 애노테이션과 현재 클래스에 정의된 애노테이션이 다르다면 모두 표시된다.
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) @Inherited public @interface AnotherAnnotation { String value() default "devhistory"; int number() default 100; }
@AnotherAnnotation("another") public class MyBook extends Book implements MyInterface{ }
Arrays.stream(MyBook.class.getAnnotations()).forEach(System.out::println); //@me.devhistory.MyAnnotation(value="devhistory", number=100) //@me.devhistory.AnotherAnnotation(value="another", number=100)
- 현재 클래스에 정의된 애노테이션(자기 자신에만 붙어있는 애노테이션) 조회
getDeclaredAnnotations()
Arrays.stream(MyBook.class.getDeclaredAnnotations()).forEach(System.out::println);
//@me.devhistory.AnotherAnnotation(value="another", number=100)
필드 애노테이션 조회
@MyAnnotation
public class Book {
@MyAnnotation private static String B= "BOOK";
@AnotherAnnotation private static final String C= "BOOK";
private String a = "a";
public String d = "d";
protected String e = "e";
@MyAnnotation
public Book() {
}
@MyAnnotation
public Book(String a, String d, String e) {
this.a = a;
this.d = d;
this.e = e;
}
private void f(){
System.out.println("F");
}
public void g(){
System.out.println("g");
}
public int h(){
return 100;
}
}
특정 애노테이션이 붙어있는 필드를 확인할 수 있고, 그 필드에 붙어있는 애노테이션의 값을 꺼내어 사용할 수도 있다.
Arrays.stream(Book.class.getDeclaredFields()).forEach(f ->{
Arrays.stream(f.getAnnotations()).forEach(a ->{
if(a instanceof AnotherAnnotation) {
AnotherAnnotation myAnnotation = (AnotherAnnotation) a;
System.out.println(myAnnotation.value());
System.out.println(myAnnotation.number());
}
});
});
//devhistory
//100
반응형
'Java > 기본' 카테고리의 다른 글
자바 다이나믹 프록시(Java Dynamic Proxy) - 1 (0) | 2021.05.31 |
---|---|
자바 리플렉션(Java Reflection) API - 클래스 정보 수정 및 실행 (0) | 2021.05.30 |
자바 리플렉션(Java Reflection) API - 클래스 정보 조회 (0) | 2021.05.29 |
자바(Java) 바이트 코드 조작관련 - 자바 에이전트 (0) | 2021.05.29 |
자바(Java) 바이트 코드 조작관련 - ByteBuddy (0) | 2021.05.27 |
[참고자료]
더 자바, 코드를 조작하는 다양한 방법, 백기선
[java-live-study] 12주차-애너테이션(Annotation)