reflection은 거울에 비친 상을 의미한다.
자바에서는 class가 실체 JVM 메모리 영역이 거울의 영역이다.
- 자바에서 컴파일러가 자바 코드를 바이트 코드로 바꾼다.
- 클래스 로더는 바이트 코드를 JVM의 메모리에 저장한다.
- 리플렉션은 JVM 메모리에 저장된 데이터를 사용하는 기술이다.
- 생성자를 통한 객체 생성
- 필드 정보 조회
- 필드 값 변경
- 메서드 정보 조회
리플렉션은 주로 프레임워크와 라이브러리에서 사용한다.
프레임워크나 라이브러리는 컴파일 시점 전까지 사용자가 생성한 객체의 타입을 알 수 없다.
이러한 문제를 동적으로 사용하기위해 리플렉션을 사용한다.
기본 생성자를 통해 객체를 만들고 값을 넣어주기 위해서 필요하다.
- 컴파일 시점이 아니라 런타임 시점에서 클래스를 분석하기 때문에, 일반 메서드 호출보다 성능이 훨씬 떨이진다.
- 컴파일 시점에서 타입 체크 기능을 사용할 수 없다.
- 내부를 노출해서 추상화를 파괴한다.
예시 코드)
public class ControllerScanner {
private final Reflections reflections;
public ControllerScanner(Object... basePackages) {
reflections = new Reflections(basePackages);
}
public List<Object> getControllers() {
// 1
Set<Class<?>> controllerClasses = reflections.getTypesAnnotatedWith(Controller.class);
return controllerClasses.stream()
.map(this::createController)
.collect(Collectors.toList());
}
private Object createController(Class<?> controllerClass) {
try {
// 2
return controllerClass.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException |
InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
@Controller
어노테이션이 붙은 클래스들을 모두 찾는다.Class.getConstructor
를 사용하여 생성자를 찾고,newInstance
를 통해 인스턴스를 생성한다.
위 코드는 스프링 코드가 아니고 내가 작성한 코드라서 정확하지 않다.
class Person {
private String name;
// 생략
}
위와 같은 dto가 존재할 때
class RequestDeserializer {
// JSON 형식의 바디를 받아서 해당 클래스 객체로 역직렬화하는 메서드
public static <T> T deserializeBody(String body, Class<T> clazz) throws Exception {
T obj = clazz.getDeclaredConstructor().newInstance();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, fieldValue);
}
}
위처럼 필드 값에 직접 값을 대입한다.