커스텀 어노테이션
Retention은 '보유'라는 뜻이다.
Retention Policy(보유 정책)은 크게 Compile과 Runtime 두개로 나뉜다.
Retention은 어느 영역까지 어노테이션을 유지시킬 것인지에 대한 기능이다.
Retention Policy를 Compile로 설정하면 Runtime 진입 시에 해당 어노테이션이 사용되지 못하도록 클래스 파일에는 해당 어노테이션이 삭제된다.
사용자가 만드는 기능은 대부분 Runtime 에 속한다.
Compile 단계에만 머무는 경우는 @Getter와 @Setter가 대표적이다. 컴파일 단계에 get, set 메소드를 만들어주고 클래스 파일에 해당 @Getter @Setter 어노테이션을 없애버리기 때문이다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultBodyStyle {
ExcelColumnStyle style();
}
커스텀 어노테이션은 메타데이터다.
메타데이터는 데이터에 대한 정보를 설명하는 데이터를 말한다.
즉, 데이터를 위한 데이터라고 할 수 있다.
데이터를 위한 데이터는 아래 글을 다 읽어보면 알게 될 것이다.
리플렉션
Member.class;
member1.getClass();
Class.forName("stir.reflection.Member");
리플렉션이란 구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API다
리플렉션은 나중에 동작하는 케이스라 JIT Compiler가 처리할 수 없다.
커스텀 어노테이션을 적용하고 사용하는 법
메소드 영역 - AOP에서 캐치하기
@Before("@annotation(com.okestro.vmwareapi.common.annotation.AutoVsphereAutomationSdkAuth)")
필드 영역 - 리플렉션으로 어노테이션 사용이 됐는지 확인하기
case 1
아래 예제는 JPA에서 JPA Auditing을 사용할 때 @CreateDate를 사용할 수 있는데 해당 기능을 커스터마이징한 예제다.
@PrePersist - JPA 엔티티가 DB에 저장되기전에 실행되는 어노테이션
@PreUpdate - JPA 엔티티가 DB에 수정되기전에 실행되는 어노테이션
JPA @PrePersist를 사용해서 가져온 엔티티인 target이 존재한다.
target 내에 존재하는 필드 중 지정한 어노테이션이 적용됐으면 값을 넣어주는 방식이다.
@PrePersist
@PreUpdate
public void setUnixTime(Object target) throws IllegalAccessException {
Field[] fields = target.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(LastModifiedDateForUnixTime.class) && field.getType().equals(long.class)) {
field.setAccessible(true);
LocalDateTime now = LocalDateTime.now();
long unixTime = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
field.set(target, unixTime);
}
}
}
JPA Entity가 DB에 저장되기전에 실행되며 getDeclaredFields()로 리플렉션을 이용해 Entity에 속한 필드들을 가져와서 isAnnotationPresent로 어노테이션이 적용된지 확인한다.
case 2
엑셀을 만들때 DTO 필드엔 값들을 List로 넣는다.
그리고 Annotation에는 Style과 컬럼이름을 미리 정해서 리플렉션을 통해 데이터를 꺼내서 사용한다.
위에서 설명한 데이터를 위한 데이터가 이 부분이다.
단 2줄만에 엑셀이 만들어진다!
(new OneSheetExcelFile 생성자 내에 리플렉션을 통해 Dto에 대한 정보를 통해 @ExcelColumn 값을 가져와서 스타일, 헤더 이름 등을 파악하여 Resource로 만들고 엑셀 render까지 전부 실행한다)
위의 소스는 확실히 이쁘다.
하지만 소스가 이쁘지 않더라도 차라리 headerName은 final static으로 상수화하고 headerStyle도 상수화해서 만든다면 리플렉션을 이용하는 것이 아니기 때문에 속도 측면에서 더 빠를 수 있다.
위의 기술은 단지 "단순화"했다라는 측면에서 롬복의 @Getter @Setter와 같은 기능이라고 볼 수 있다.
Getter Setter도 직접 만들면 더 빠르지 않겠는가!
그래서 속도가 빨라야되는 엑셀 구현은 직접 구현하되 단순하고 느려도 상관없는 부분은 위의 리플렉션 소스를 사용하는 것이 좋다.
개인적으로 오픈소스를 만들 것이 아니고 회사 차원에서 사용한다면 리플렉션으로 기능의 단순화는 안하는 것이 더 좋을 수도 있다.(2개의 방법이 다 있으면 다 배워야하잖아..)
만약에 그 회사가 크고 여러곳에서 사용해야 할 것 같다면 리플렉션으로 구현하는 것도 하나의 방법이다.
@Getter
@RequiredArgsConstructor
public enum CarColumnInfo implements ExcelColumnInfo {
COMPANY("회사", 0, 0, ColumnType.TEXT),
NAME("차종", 0, 1, ColumnType.TEXT),
PRICE("가격", 0, 2, ColumnType.NUMBER),
RATING("평점", 0, 3, ColumnType.NUMBER);
커스텀 어노테이션과 리플렉션을 안쓴다면 위와 같이 Enum으로 관리해서 사용할 수 있다.
이렇게 값들을 Enum으로 관리해서 만드는게 더 속도는 빠를 것이다.
'☕ Java' 카테고리의 다른 글
[Kotlin] Companion Object (0) | 2024.02.02 |
---|---|
Java Stream 사용법 ( 당신의 Stream은 안녕하십니까? ) (0) | 2023.08.26 |
[Kotlin] Optional vs Kotlin Nullable 문법 비교하기 (0) | 2023.06.21 |
java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter (0) | 2023.06.21 |
[Java] JVM Warmup (0) | 2023.03.12 |