Today I Learned
(24.06.18)[10주차] Reflection & ReflectionTestUtils을 통한 필드 다루기
@Entity
@Getter
@Table(name = "users")
public class User {
...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, name = "username")
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String name;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "user_info")
private String userInfo;
@Column(nullable = false)
...
- 위 코드는 @Column의 필드가 private으로 접근제어자로 설정된 임의의 Entity 의 일부
- Setter 또는 오버라이드된 생성자를 통해 캡슐화된 상수들의 값을 설정할 수있는데 위의 엔터티에서는 존재하지 않음
- 실제 로직을 구성하는 코드에는 상수 값에 대한 설정이 사용되지는 않지만, Test Code 등에 임의로 생성이 필요할 때가 존재 -> 그렇다면 @Setter @Builder 또는 생성자를 사용하지 않고 사용하는 방법으로 Reflection 기능을 사용
Reflection & ReflectionTestUtils을 통한 필드 다루기
Reflection
- Java의 기본 기능 중 Runtime 시점에서 클래스에 직접적으로 확인 및 조작하는 데 사용하는 기능
- 객체의 생성 & 구조를 분석 하는데 사용
- 그러므로 유연성 있게 사용이 가능
- 접근제어자 상관 없이 직접적으로 클래스단위에서 조작에 대해서는 Runtime 시점에서 치명적인 오류가 발생가능성이 있음
- 그러므로 디버깅을 통해서 Reflection으로 인한 오류 발생을 잡을 수 없음
Reflection 클래스 단위에서의 주요 기능
- getClass()
- 생성된 객체로 해당 클래스를 얻는 메서드
- Object obj = new String() 로 선언이 되었다면 obj.getClass() 일 때, String 을 반환
- 생성된 객체로 해당 클래스를 얻는 메서드
- Class.forName("클래스이름")
- 해당 클래스이름(또는 패키지)에 해당하는 클래스를 동적으로 로드하고 "클래스이름".class 형태의 Class 객체를 반환하는 메서드
- .class
- 클래스 리터럴 Class Literal로 컴파일할때 그 타입을 나타내는데 사용하는 것과 동일 한 것으로 Class 객체를 반환시킬 때 Java 코드내에서도 직접적으로 상용할 수 있음
- 얻어진 Class 객체에서는 클래스에 정의된 멤버 이름, 갯수 파악, 클래스 객체를 통한 객체 생성(.newInstance() 메서드) 및 내부 메서드 호출 모든 것이 가능해짐
- .class
- 해당 클래스이름(또는 패키지)에 해당하는 클래스를 동적으로 로드하고 "클래스이름".class 형태의 Class 객체를 반환하는 메서드
Reflection 필드 단위에서의 주요 기능
MyClass myClass = new MyClass();
Field field = MyClass.class.getDeclaredField("name");
Object fieldValue = field.get(myClass);
field.setAccessible(true);
field.set(myClass, "클래스")
- getDeclaredField("필드이름")
- 클래스 객체에서 해당 필드이름을 가진 정보를 가지고 올 수 있는 매서드
- Field field = User.class.getDeclaredField("id"); 일 경우, User 클래스에서 id 필드를 가지고 오는 것
- 클래스 객체에서 해당 필드이름을 가진 정보를 가지고 올 수 있는 매서드
- (필드객체).get(대상클래스객체)
- getDeclareField로 만들어진 Field 객체를 통해 필드값을 대상 클래스 객체에선 찾아서 반환하는 메서드
- (필드객체).set(대상클래스객체, "해당필드에 설정할 값")
- getDeclareField로 만들어진 Field 객체에서 설정하고 싶은 값을 해당 클래스에서 설정할 수 있는 메서드
- 단, private 접근제어자 일 경우에는 .setAccessible(true) 를 꼭 붙일 수 있도록 해야함
ReflectionTestUtil
- Java의 Spring Framework 에서 제공되어지는 클래스로 Reflection의 기능을 대신 할 수 있도록 하는 클래스
- 특히, Test Code내에서 특정 private 접근제어자 필드에 대해서 설정을 하는데 주요 사용
- 테스트 대상 클래스의 내부 필드를 조정하는 기능
- 복잡한 Reflection의 구조, 오류발생 여부, 오래 걸리는 작동 시간 등을 고려했을 때, Spring 환경에서의 테스트할 때는 Reflection 기본 보다는 RelfectionTestUtil을 사용할 수 있도록 해야
ReflectionTestUtil 주요 기능
MyClass myClass = new MyClass();
String name = (String) ReflectionTestUtils.getField(myClass, "name");
ReflectionTestUtils.setField(myClass, "name", "클래스");
- .getField(대상클래스객체, "값을 얻어올 필드이름")
- 특정 클래스 객체에서 특정 필드의 값을 얻어올 수 있는 메서드
- .setField(대상클래스객체, "값을 지정할 필드이름", 지정할값)
- 특정 클래스 객체에서 특정 필드의 값을 지정할 수 있는 메서드
- 접근제어자가 private인 필드도 당연히 사용이 가능
- 원래는 .getField(대상클래스객체, 해당클래스타입(클래스 리터럴), "값을 지정할 필드이름 ", 지정할값, 필드클래스타입(클래스리터럴) ) 이지만 Java가 작동할 때 이미, 해당 객체를 불러오면서 타입을 이미 알고 있기 때문에, 굳이 쓰지 않아도 됨
- 특정 클래스 객체에서 특정 필드의 값을 지정할 수 있는 메서드
자바의 정석에서도 Reflection API 정도로만 간단히 소개가 되어서 실제로 Test Code를 사용하면서 따로 찾아보면서 학습을 해야하는 부분이라 꼼꼼하게 찾아보면서 정리를 했다.
'Today I Learned' 카테고리의 다른 글
(24.06.20)[10주차] Optional 객체값 가져오기 (0) | 2024.06.20 |
---|---|
(24.06.19)[10주차] Mockito 구문 (0) | 2024.06.19 |
(24.06.17)[10주차] Validator 객체 생성과 테스트 코드에서의 활용 (0) | 2024.06.17 |
(24.06.14)[9주차] Servlet 객체 활용하기 (0) | 2024.06.14 |
(24.06.13)[9주차] Spring 심화 강의 02 (마무리) (1) | 2024.06.13 |