Reflaction


최근 자바 웹 프로그래밍 기초를 학습하면서 Servlet, JSP를 지나 기본적인 Spring MVC 모델을 따르는 미니 MVC 프레임워크 를 만드는 실습중이다.

전체적인 흐름이 익숙했던 터라 실습을 하거나 예제 코드를 따라가는데에는 전혀 무리가 없었기에 따로 블로그 포스팅도 미뤄두고 있었는데 언젠가는 공부해야지.. 했던 Reflaction API 를 활용하는 부분에서 너무 많이 헤메었다.

이후 예제 코드도 포스팅 할 예정이지만 그 전에 Reflaction API에 대한 개념부터 살펴보기 위해 포스팅을 한다.


Class class


public final class Class implements ... {} //Class 클래스

객체지향의 사실과 오해를 읽으면서 객체지향에서 클래스 는 단순히 자율적인 행동을 하는 객체 를 구현하기 위한 메커니즘일 뿐이라는 것을 알고 있지만 자바의 Reflaction 을 이야기 하기 위해서는 Class 에 대한 이야기가 어쩔수 없이 많이 나온다.

먼저 Class class 에 대해서 이야기 해보자.

Class class 는 자바에서 사용되는 클래스에 대한 구조 를 가지고 있는 Class 이다.

Class 라는 명칭과 내연적 의미(intension)에 대해 생각해 보면 어렵지 않게 이 클래스의 추상을 알 수 있다. (물론 활용과 구현자체는 쉽지 않을 수 있지만)

우리는 하나의 어플리케이션을 만들 때 다양한 객체들끼리의 협업을 고려하고 해당 객체들이 어떤 책임을 갖느냐에 따라 역할을 부여하기도 하고 객체들은 협업을 위해 적절히 부여된 책임을 수행한다.

그리고 객체마다 서로 다른 책임과 상태를 갖는것은 당연하다.

하지만 Java에서 이러한 객체들은 모두 클래스 라는 메커니즘으로 구현된다.

즉, 멤버 변수(상태), 메서드(책임 수행을 위한 행동 양식), 생성자 등 서로 다른 클래스로 구현된 객체들이지만 모두 동일한 메커니즘을 갖는다.

Class class 는 이러한 메커니즘 에 대한 구조를 표현한다.

즉, 클래스들의 구조를 표현한다. (어떤 멤버를 갖고 어떤 메서드를 갖는지 등)

이제 Class 객체의 이야기로 넘어가보자.

Class 객체자신이 속한 타입(클래스)의 모든 정보 를 담고 있으며 클래스 당 1개씩만 존재 한다.

.class 파일 이 Class Loader(클래스 로더)에 의해서 메모리에 올라갈 때 Class 객체가 자동으로 생성된다.

.class 파일 은 그 자체로 해당 클래스의 구조를 나타내는데 최초 Class Loader가 이 파일에서 정보를 추출해서 Class 객체 로 만드는 것이다. (클래스에 대한 메타 정보. 즉 멤버변수, 메서드, 생성자와 같은 정보를 갖고있다.)

자바는 모두 알다시피 JVM(Java Virtual Memory) 위에서 동작한다.

간단한 Main을 갖는 Java Application이든 Serlvet 기반의 WAS를 활용하든 결국엔 JVM 위에서 동작한다.

Class class 는 JVM에서 동작하는 클래스의 정보를 묘사 하기 위해 사용되는 일종의 메타 클래스(Meta-Class) 이다.

다시말해 JVM에 로드 될 각 클래스에 대한 정보를 담고 있는 클래스 이다. 여기서 말하는 클래스에 대한 정보란 클래스 명, 멤버 필드, 멤버 메소드, 클래스의 종류(인터페이스 등) 을 의미한다.

먼저 보편적으로 클래스를 인스턴스 화 해서 하나의 객체를 생성해 내는 과정을 하나하나 그려보자.

User user = new User();

라는 코드가 있다라고 가정하면

JVM은 우선 USER 클래스가 JVM 에 로드 되었는지 확인한다. 만일 로드가 되어있지 않다면 사용 가능한 Class Loader 를 이용해서 User 클래스 를 JVM상에 로드하려 할 것이다.

이 때 .class가 존재시 Class Loader 는 이 .class 파일을 읽어서 Class 객체 로 만변환한다.

그러니까 .class 라는 파일 형태로 저장되어 있는 클래스를 읽어서 Class 클래스에 정의된 형식으로 변환 하는 것이다.

다시말해 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것 이 바로 Class 객체이다.

만일 Class Path에 문제가 있다거나 실제 컴파일 된 .class 파일이 없다면 ClassNotFoundException 이 나타날 것이다.

USER 클래스가 JVM에 로드되었다면 이제 User 인스턴스를 하나 생성하고 생성자 를 호출한다.

그 다음 user 라는 참조변수는 생성된 인스턴스의 주소를 가리키게 된다. (user 자체가 인스턴스가 아니라 실제로는 인스턴스의 주소를 저장한다는 사실을 기억하라.)

위 시나리오에서 알 수 있듯, 새로운 인스턴스 생성 을 위해서는 클래스의 정보 가 필요하다. 그리고 이 클래스의 정보는 JVM 에서 관리 가 되고있다.

그렇다면

Class<?> clazz = Class.forName("my.test.User");
User user = (User)clazz.newInstance();

와 같은 인스턴스 생성은 어떻게 이루어질까?

흔히 볼 수 있는 리플랙션을 이용한 동적 로딩 이다.

동적 로딩의 대상(User)의 Meta-Class 정보인 Class 인스턴스 를 가져와서 참조변수 clazz 에 할당한다.

그렇다면 clazz 가 동적 로딩이 된 User 인스턴스인가? 그렇지 않다. 위에서 계속 언급했듯이 Class class클래스의 메타 정보 를 담고있는 그릇이다. 즉, User 클래스의 메타 정보 를 담고있는 그릇 이다.

그리고 이렇게 가져온 메타정보를 이용해서 우리는 당연하게도 인스턴스를 생성할 수 있다.

왜? 메타 정보에는 해당 클래스의 생성자를 비롯한 모든 정보를 의미하기 때문이다.

그렇기 때문에 User user = (User)clazz.newInstance() 를 통해서 clazz 로 부터 생성자 호출 메서드를 작동함으로써 User 객체 를 생성할 수 있다.


참고 및 출처