오늘 포스팅에서 알아볼 내용은 Java Reflection입니다.
우선 Java는 Compile 언어입니다.
Java에서는 동적으로 객체를 생성하는 방식이 없었기에 Reflection 방식으로 객체를 동적으로 생성할 수 있게 되었습니다.
Java Reflection??( Reflection : 반사, 굴절, 투명)
Java Reflection은 다음과 같은 정보를 분석하거나 가져올 수 있습니다.
- Class
- Constructor
- Method
- Field
이러한 정보들을 가져와 객체를 생성하거나 메서드를 호출할 때 변수의 값을 변경할 수 있습니다.
Java Reflection API의 기능으로는 객체의 타입을 모르고 객체의 메모리 주소 값만 알고 있는 상태에서 객체의 메서드, 타입, 변수에 접근 가능하게 해주는 Java API입니다.
Java의 다형성을 이용한 Reflection 예시 코드
Person Class 정의
public class Person{
private final String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void increaseAge(){
this.age++;
}
public int getAge(){
return age;
}
}
Main Method
public static void main(String [] args){
Object obj = new Person("juwon", 0);
// cannot find symbol 에러 발생
obj.increaseAge();
}
자바는 Compile 언어기 때문에 Compile시에 객체의 타입이 결정됩니다. 따라서 obj라는 이름의 객체는
Object로 타입이 결정되었기 때문에 Object의 인스턴스 변수와 메서드만 가능합니다.
따라서 자바는 구체적인 클래스를 모르면 해당 클래스의 정보에 접근할 수 없다는 것을 알 수 있습니다.
이렇게 불가능한 방법을 가능하게 해 주는 것이 Reflection API입니다.
위의 예제의 같은 상황에서 Reflection API를 활용해 Person Class의 메서드를 호출해보겠습니다.
public static void main(String [] args) throws Exception{
Object obj = new Person("juwon", 0);
Class personClass = Person.class;
Method age = personClass.getMethod("increaseAge");
// invoke(호출할 메소드의 객체, 해당 메소드의 파라미터)
age.invoke(obj, null);
Method getAge = personClass.getMethod("getAge");
int age = (int)getAge.invoke(obj, null);
System.out.println("나이는 " + age + "살입니다. ");
// 나이는 1살입니다.
}
increaseAge 메서드가 실행되어 0으로 초기화했던 Person 객체의 Age가 1로 출력되는 것을 알 수 있습니다.
위의 방법은 Java Reflection API로 객체의 구체적인 Person 타입을 알지 못해도 increaseAge메서드에 접근하였습니다.
따라서 Reflection API는 클래스의 이름만 가지고 생성자, 필드, 메서드 등의 클래스에 대한 정보를 가져올 수 있는 기능입니다.
어떻게 이런 방법이 가능할까요???
Reflection API의 작동원리
JVM Run-time 동안 개발자가 작성한 소스코드가 컴파일러를 거쳐 바이트 코드로 변환되어 static 영역에 저장되었을 때
Reflection API는 이 바이트 코드를 활용합니다.
그래서 클래스 이름만 알고 있다면 static 영역에서 클래스의 정보를 가져올 수 있는 것입니다.
하지만 대부분의 경우 사용하지 않는 것을 권장합니다.
대표적인 단점 중 하나인 성능 오버헤드가 있습니다.
자바 컴파일 타임이 아닌 런타임에 동적으로 타입을 분석하고 정보를 가져오므로 JVM을 최적화할 수 없기 때문입니다.
뿐만 아니라 직접 접근이 불가능한 private 인스턴스 변수, 메서드에 접근하기 때문에 내부에 노출하면서 동시에
추상화가 사라집니다.
그렇다면 어디에서 주로 사용할까요???
Reflection API의 적용 예
Java Relection API는 사용자가 어떤 클래스를 만들지 예측할 수 없을 때, 프레임워크 혹은 라이브러리가 동적으로 로딩하는 방법에서 많이 사용됩니다.
실사용 예시로는 Spring Framework, jackson 라이브러리, Hibernate, Intellij 자동완성 등에서 Reflection을 사용하고 있습니다.
Spring Framework에서의 Reflection API 사용 예로는 Spring Container의 BeanFactory가 있습니다.
Java Bean은 Application을 실행한 후 런타임 동안 객체가 호출될 때 동적으로 객체의 인스턴스를 BeanFactory에서
Reflection을 사용합니다.
Reflection API로 접근할 수 없는 정보 중 하나가 생성자의 매개변수입니다.
그렇기에 Spring Data JPA에서의 Entity에 기본( Default ) 생성자가 필수인 이유도 동적으로 객체 생성 시에 Reflection API를 활용해 기본 생성자로 객체를 생성하면 인스턴스 변수의 값에 접근할 수 있기 때문입니다.
Reflection API는 Spring Framework에서 구체적이지 않은 객체를 동적으로 해결해주는 기능입니다.
오늘은 Java Reflection API에 대해 알아보았습니다.
'Backend > Java' 카테고리의 다른 글
Java Servlet Class를 알아보자! (0) | 2022.01.23 |
---|---|
JVM( Java Virtual Machine )을 알아보자! (0) | 2022.01.12 |