본문 바로가기
JAVA

[JAVA] Reflection

by doodoom 2022. 10. 6.

0. 이 글을 쓰게 된 이유

Spring을 학습 하다보니 자바의 리플렉션을 접하게 되었다. 리플렉션에 대해서 자세하게는 모르는 상태였어서 이번에 새로 학습하게 된 리플렉션에 대해서 정리 해보려고 한다.

1. Java Reflection API란?

구체적인 클래스 타입을 알지 못해도 그 클래스의 정보에 접근 및 조작할 수 있게 하는 API

일반적인 상황이라면 잘 이해가 안되는 정의이다.
자바에서는 구체적인 클래스가 무엇인지 알아야지 그에 대한 메서드, 타입, 변수 등에 접근할 수 있다.
하지만 reflection은 구체적인 클래스 없이도 그에 대한 정보를 모두 가져오고 심지어 조작까지 할 수 있다고 한다.
이게 무슨 뜻인지 예시를 통해 알아보자.

2. 예시

다음과 같은 Pizza 클래스가 있다고 하자.

public class Pizza {
    private String name;
    private ArrayList<String> toppings;

    public Pizza(String name) {
        this.name = name;
        toppings = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public ArrayList<String> getToppings() {
        return toppings;
    }

    public void addTopping(String topping) {
        toppings.add(topping);
    }
}

모든 객체는 Object를 상속 받고 있으므로 다음과 같이 객체 생성이 가능하다.

public static void main(String[] args) {
        Object obj = new Pizza("하와이안 피자");
}

이 객체는 Object 타입으로 생성했기 때문에 Pizza 클래스에만 있는 정보(메소드, 변수 등)에 접근할 수 없다.
즉, 구체적인 클래스가 무엇인지 모르기 때문에 접근할 수 없는 것이다.(컴파일 에러를 띄움)

이처럼 불가능한 일을 가능하게 해주는 것이 Reflection이다.
다음은 reflection api를 이용해서 Pizza 정보에 접근하는 코드이다.

public static void main(String[] args)
        throws Exception {
        Object obj = new Pizza("하와이안 피자");
        Method addTopping = obj.getClass().getMethod("addTopping", String.class);
        addTopping.invoke(obj, "햄");

        Method getToppings = obj.getClass().getMethod("getToppings");
        ArrayList<String> toppings = (ArrayList) getToppings.invoke(obj);
        System.out.println(toppings);
        // 출력 : [햄]

        Field name = obj.getClass().getDeclaredField("name");
        name.setAccessible(true);
        String s = (String) name.get(obj);
        System.out.println(s);
        // 출력 : 하와이안 피자
    }

마법 같게도 위와 같은 방법으로 Pizza 클래스의 정보를 자유자재로 접근할 수 있다.
심지어 private으로 설정 되어있는 name 필드에도 접근하는 것을 볼 수 있다.

JVM은 실행이되면 클래스 코드가 컴파일러를 거쳐 바이트코드가 되어 Method 영역(Static 영역)에 로드 된다.
Reflection api는 static 영역에 있는 이 클래스를 가져올 수 있다.

Reflection의 치명적인 단점

Reflection은 강력하지만 무분별하게 사용해서는 안된다. Reflection을 사용하지 않고 작업을 수행할 수 있는 경우에는 사용을 피하는 것이 바람직하다. - Oracle java 공식 문서-

자바 공식 문서에서 위와 같이 경고를 한다. 무슨 단점이 있을까?

  1. 성능 오버헤드
    리플렉션은 컴파일 타임이 아닌 동적으로 동작한다. 그래서 jvm에서 최적화가 불가능하다.
    결과적으로 리플렉션을 사용하지 않는 방식에 비해 리플렉션은 성능이 느리며 자주 호출되어서는 안된다.
  2. 클래스 내부 노출
    예시에서 봤듯이 리플렉션은 private 접근자가 붙은 필드도 자유롭게 드나들 수 있다.
    이는 특별한 경우가 아니라면 굉장히 위험할 수 있다.
  3. 보안
    리플렉션은 자바에서 제공하는 보안 관리자를 피해 런타임 권한이 필요로한다. 그러므로 안전하지 않게 동작할 수도 있다.

그러므로 우리는 평소에 굳이 리플렉션을 사용할 일이 없다. 혹시나 어쩔 수 없이 사용할 경우 위와 같은 이슈들을 꼭 고려하여 사용해야한다.

Reflection 활용

리플렉션은 자바만이 가지고 있는 굉장히 강력한 기능이다.
런타임 시에 클래스의 정보를 수정할 수 있으니 확장성이 높고, 다이나믹한 프로그래밍을 할 수 있다.
리플렉션은 프레임워크나 라이브러리에서 많이 사용된다. Spring 같은 경우에 프록시 팩토리 빈에서 사용이 된다.

그럼 일반적인 어플리케이션 개발자는 리플렉션을 몰라도 될까? 아니다.
프레임워크를 사용한다는 것은 다른 의미로 프레임 워크를 확장한다는 말이다. 리플렉션 활용 방법을 알아서 필요할 때 활용할 수 있다.

'JAVA' 카테고리의 다른 글

[JAVA] TDD란?  (0) 2023.04.09
[JAVA] 불변 클래스  (0) 2023.03.24
[Java] 객체 지향 vs 성능  (0) 2023.03.09
[JAVA] 객체 안의 public api 선정 기준  (0) 2023.02.28
[JAVA] equals와 hashcode  (0) 2022.09.28