티스토리 뷰
참고: https://docs.oracle.com/javase/tutorial/java/annotations/
Java Annotation
Annotations, a form of metadata, provide data about a program that is not part of the program
메타데이터의 한 형태인 어노테이션은 프로그램의 일부가 아닌 프로그램에 대한 데이터를 제공합니다.
- 컴파일러를 위한 정보 - 어노테이션은 컴파일러가 에러나 supress warnings를 찾기 위해 사용된다.
- Compile-time 그리고 deployment-time 가공과정 - 소프트웨어 도구들은 어노테이션 정보들을 활용하여 코드, XML, JSON 등을 생성한다.
- 런타임 처리 - 몇몇 어노테이션은 런타임 시간에 검사되는 데 사용된다.
Declaring an Annotation Type
실제 프로젝트를 진행하면서 어떤 클래스에 중요한 정보를 저장해야 할 때 다음과 같이 표현할 수 있다.
public class MyClass {
// Author: Park Suhan
// Date: 2022/10/01
// Current Version: 1.5.6
// Last Modified: 2022/10/30
// By: Park Suhan
// Reviewers: Mike, Sandy, Alice
}
하지만 어노테이션을 가지고 메타데이터를 더한다면, 다음과 같이 정의할 수 있다.
@interface ClassRecord {
String author();
String date();
double currentVersion() default 1.0;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
String[] reviewers();
}
어노테이션 타입 정의는 인터페이스 정의하는 방식 ( 앞에 '@'붙였다는 사실만 다를 뿐)과 비슷하다.
타입을 다음과 같이 설정했으면 우리는 어노테이션 타입에 대한 값 설정을 다음과 같이 할 수 있다.
@ClassRecord(
author = "Park Suhan",
date = "2022/10/01",
currentVersion = 1.56,
lastModified = "2022/10/30",
lastModifiedBy = "Park Suhan",
reviewers = {"Alice", "Sandy", "Mike"}
)
public class MyClass {
}
이 때 약간의 팁을 주자면 해당 어노데이션은 문서의 성향이 있으며 Javadoc을 원한다면 다음과 같이 추가해주면 더 좋다.
@Documented
public @interface ClassRecord {
// 생략
}
Predefined Annotation Types
@Deprecated, @Override, @SuppressWarnings
@Deprecated
Deprecated 어노테이션은 해당 클래스 또는 메소드를 더 이상 사용하지 않을 것이라고 표현하는 메타데이터이다. 컴파일러가 @Deprecated 어노테이션 표시된 클래스, 메소드 등을 프로그램에서 사용할 때 사용하지 말라고 경고문을 날린다.
@Override
@Override 어노테이션은 컴파일러에게 부모 클래스로부터 상속받은 메소드라는 정보를 준다. @Override 어노테이션은 의무적으로 사용해야 하는 것은 아니지만, 사용할 경우 에러를 예방할 수 있다.
@SuppressWarnings
SuppressWarnings 어노테이션은 특정 경고 메시지를 없애는 데 사용된다. 해당 사용되는 곳은 컬렉션 프레임워크를 보면 심심치 않게 볼 수 있다.
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
모든 컴파일러 경고는 하나의 카테고리에 속하게 되는데 "unchecked", "deprecation"이 이에 속한다.
@SafeVarags
SafeVarags 어노테이션은 메소드나 생성자에 지원되는 어노테이션으로 코드가 varargs 매개변수에 대해 잠재적으로 안전하지 않은 작업을 수행하지 않음을 나타냅니다
https://www.baeldung.com/java-safevarargs
@FunctionalInterface
FunctionalInterface 어노테이션은 람다를 사용할 때 컴파일 에러를 체크하기 위해 사용되는 어노테이션으로서 메소드를 하나만 정의하도록 설정하는 어노테이션입니다. 두개 이상이 정의되었을 때 에러 표시가 나면서 실수를 줄일 수 있게 해줍니다.
Annotation을 지원하는 Annotation
@Rentention
어느 시점까지 유지할 것인지 표시하는 어노테이션
- RetentionPolicy.SOURCE – 소스 코드 상까지는 유지되며 그 이후 (컴파일러 이후) 부터는 효력이 없음
- RetentionPolicy.CLASS – 컴파일 시점까지 유지되며, 그 이후 부터는 무시(Java Virtual Machine (JVM)에 의해)
- RetentionPolicy.RUNTIME – JVM시점 부터 유지되며 런타임때 사용가능하다.
@Documented
JavaDoc 관련 문서화할 때 사용되는 어노테이션
@Target
적용되는 어노테이션의 범위를 제한하는 어노테이션
- ElementType.ANNOTATION_TYPE -> annotation type.
- ElementType.CONSTRUCTOR -> constructor.
- ElementType.FIELD -> field or property.
- ElementType.LOCAL_VARIABLE -> local variable.
- ElementType.METHOD -> method-level annotation.
- ElementType.PACKAGE -> package declaration.
- ElementType.PARAMETER -> parameters of a method.
- ElementType.TYPE -> any element of a class.
@Inherited
어노테이션 상속을 허용해주는 어노테이션. 기본적으로 어노테이션은 상속 불가능하다. (자세한 것은 밑의 예시를 확인할 것)
@Repeatable
하나의 예시를 보자
Name.java
해당 어노테이션은 값을 저장하는 어노테이션으로 중복해서 사용할 수 있는 어노테이션이다.
중복된 값을 가져올 때는 Names.class를 사용하라는 표시로 @Repeatable를 표시한다.
@Target(ElementType.TYPE)
@Repeatable(Names.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
String value();
}
Names.java
Name 어노테이션들을 관리하는 어노테이션으로 사용하는 코드에서 해당 어노테이션을 통해 정보를 가져온다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Names {
Name[] value();
}
@Name("A")
@Name("B")
@Name("C")
public class TargetName {
}
public class Runner {
public static void main(String[] args) {
TargetName targetName = new TargetName();
Names names = targetName.getClass().getAnnotation(Names.class);
Name[] nameArray = targetName.getClass().getAnnotationsByType(Name.class);
Name[] values = names.value();
for (Name value : values) {
System.out.println(value);
}
System.out.println(Arrays.toString(nameArray));
}
}
@com.playground.springplayground.annotation.Name("A") @com.playground.springplayground.annotation.Name("B") @com.playground.springplayground.annotation.Name("C") [@com.playground.springplayground.annotation.Name("A"), @com.playground.springplayground.annotation.Name("B"), @com.playground.springplayground.annotation.Name("C")]
사용하는 방법은 다음 두가지 정도 있다.
- Name을 관리하는 Names를 통해 가져오는 방법
- Name을 지정하여 Array로 받는 방법
취향이니 좀 더 끌리는 것을 선택하면 좋을 것 같다.
Spring Annotation
@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@Target(ElementType.TYPE)
해당 어노테이션은 클래스에만 사용가능하도록 한정시키는 역할
@Retention(RetentionPolicy.RUNTIME)
JVM 동작하는 시점에 적용되며, 흔히 런타임 시점에서 동작한다고 생각하면 된다.
@Inherited
적용된 어노테이션의 A 클래스가 존재한다고 했을 때 A 클래스를 상속한 B 클래스에서 A 클래스에 적용된 메타 데이터를 사용할 수 있게 해주는 것이 @Inherited 이다.
예시
@Inherited 를 적용한 클래스
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherit {
String value() default "hi";
}
@Inherited 를 적용하지 않은 클래스
public @interface NoInherit {
String value() default "noInherit";
}
적용한 클래스
@NoInherit
@MyInherit
public class AClass {
}
테스트할 클래스
public class BClass extends AClass{
}
실행 결과
public class Runner {
public static void main(String[] args) {
final BClass bClass = new BClass();
Annotation[] annotations = bClass.getClass().getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
@com.playground.springplayground.annotation.inherited.MyInherit("hi")
보시는 바와 같이 @Inherited 어노테이션을 적용한 곳에서만 값을 가져올 수 있는 것을 볼 수가 있다.
이 점을 유의해가면 사용하시길 바란다.
@AliasFor
@AliasFor 에 정의된 어노테이션의 메타데이터 값을 가져오는 역할을 한다.
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
"해당 부분은 SpringBootApplication 어노테이션에 있는 exclude method의 값은 EnableAutoConfiguration 어노테이션의 exclude 값의 역할을 부여한다." 라고 이해하면 된다.
하나의 예시를 들어보자
MyRequestMapping.java
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping
public @interface MyRequestMapping {
@AliasFor(annotation = RequestMapping.class, attribute = "value")
String route() default "";
@AliasFor(annotation = RequestMapping.class, attribute = "method")
RequestMethod[] define() default {};
}
MyRequestMapping은 RequestMapping의 일부분을 재정의한 어노테이션으로서 RequestMapping의 value 부분은 route로 재정의하였으며, method 부분은 define을 재정의하였다.
@RestController
public class AnnoController {
@MyRequestMapping(define = RequestMethod.GET, route = "/web/annotation")
public String view() {
return "view";
}
}
해당 부분을 테스트 해보면 view가 정상적으로 찍히는 것을 알 수 있다.
어노테이션에 대한 이해는 이 정도 하면 될 것 같다.
나중에 하면서 부족하거나 필요한 지식은 추가해나갈 것이다.
읽어주셔서 감사합니다.
'JVM > JAVA' 카테고리의 다른 글
[Java] Thread 이해해보기 (0) | 2022.10.10 |
---|---|
[Java] Generic 에 대한 이해 (0) | 2022.10.06 |
[JAVA] [ArrayList] removeIf에 대해서 (0) | 2022.01.31 |
[JAVA] ArrayList thread safe하지 않다는 뜻이 뭘까? (0) | 2022.01.08 |
[JAVA] Singleton + multiThread 를 취할 때 고려해야 할 것 (0) | 2021.12.23 |
- Total
- Today
- Yesterday
- ubuntu
- Java
- thread
- env
- 2021 KAKAO BLIND RECRUITMENT
- 파이썬
- Collections
- docker
- 알고리즘
- django
- Python
- Spring
- 프로그래머스
- DRF
- 자바
- Pattern
- Command Line
- 면접
- 그래프
- PostgreSQL
- docker-compose
- setattr
- headers
- 카카오
- Linux
- BFS
- dockerignore
- 백준
- postgres
- Celery
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |