JVM의 ClassLoader을 통해 힙 영역에 로딩된 Class 타입의 객체를 동적으로 사용하기 위한 방법
동적으로 어떤 클래스에 있는 필드 값을 get하거나 혹은 set할 때, 필드 데이터의 순서를 보장하지 않고 가져오는 이벤트가 생김.
이를 어노테이션을 통해 해결한 방법을 기록함.
1. Order Annotation 생성
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
int value();// value of order
}
2.Order Annotation을 적용한 Dto class 생성(테스트를 위해 순서 변경)
@Getter
@Setter
public class MemberDto {
@Order(value = 1)
private String id;
@Order(value = 3)
private String password;
@Order(value = 2)
private String username;
private String address;
}
3. 필드 데이터를 가져오기 위한 Enum과 Annotation 정렬을 위한 메서드 생성
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@Getter
@RequiredArgsConstructor
public enum DtoFieldData {
Member(MemberDto.class.getSimpleName(), Arrays.asList(fieldSetting(MemberDto.class
.getDeclaredFields())));
private final String dtoName;
private final List<Field> fields;
/**
* Order by OrderAnnotation of value
* @param fields
* @return Field[]
*/
private static Field[] fieldSetting(Field[] fields) {
Arrays.sort(fields, new Comparator<Field>() {
@Override
public int compare(Field o1, Field o2) {
Order or1 = o1.getAnnotation(Order.class);
Order or2 = o2.getAnnotation(Order.class);
if (or1 != null && or2 != null) return or1.value() - or2.value();
// Nullable
if (or1 != null && or2 == null) return -1;
if (or1 == null && or2 != null) return 1;
return o1.getName().compareTo(o2.getName());
}
});
return fields;
}
}
위 3가지 방법을 통해서 MemberDto에 정의된 value 값으로 정렬을 시켜서 DtoFieldData의 Enum으로 저장.
위를 테스트하기 위한 코드를 작성 함.
4. 테스트 코드 작성
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
public class ReflectionOrderTest {
private String[] input;
@BeforeEach
@DisplayName("기본 데이터 세팅")
void setUp(){
input = makePolicyMessage();
}
@Test
@DisplayName("Order 을 통해 리플렉션 필드값 set 테스트")
void testByReflection() throws IllegalAccessException {
List<Field> fieldList = DtoFieldData.Member.getFields();
MemberDto memberDto = new MemberDto();
for (int i = 0; i < fieldList.size(); i++) {
Field field = fieldList.get(i);
field.setAccessible(true);
field.set(memberDto, input[i]);
}
verify(memberDto);
}
/**
* 검증
* @param memberDto
*/
void verify(MemberDto memberDto) {
assertThat("1").isEqualTo(memberDto.getId());
assertThat("user1").isEqualTo(memberDto.getUsername());
assertThat("password1").isEqualTo(memberDto.getPassword());
}
/**
* 임의의 로그 데이터 생성
* @return String[]
*/
private String[] makePolicyMessage() {
String[] input = new String[DtoFieldData.Member.getFields().size()];
String id = "1";
String username = "user1";
String password = "password1";
input[0] = id;
input[1] = username;
input[2] = password;
return input;
}
}