Reflection

Soonwoo Kwon·2023년 12월 4일

Reflection API

Java Reflection은 런타임에 클래스, 인터페이스, 필드, 메소드에 대한 정보를 조회하고 조작할 수 있게 해주는 Java API이다.
이를 통해 개발자는 컴파일 시점에는 알 수 없는 클래스의 객체를 조사하고, 동적으로 접근할 수 있다.

Reflection API 사용법

1-1. 이름을 통한 Class 객체 접근

Class<?> stringClass = Class.forName("java.lang.String");
System.out.println(stringClass.getName());  // 출력: java.lang.String

1-2. 객체를 통한 클래스 이름 찾기

String s = "example";
Class<?> classObj = s.getClass();
System.out.println(classObj.getName());  // 출력: java.lang.String

2. 메서드 접근 및 호출

Method toUpperCaseMethod = String.class.getMethod("toUpperCase");
String result = (String) toUpperCaseMethod.invoke("hello");
System.out.println(result);  // 출력: HELLO
  • 이름을 통해 특정 클래스의 메서드 객체를 얻어 실행

3. 필드 접근 및 수정

class Example {
    private String hidden = "initial";
}

Example example = new Example();
Field hiddenField = Example.class.getDeclaredField("hidden");
hiddenField.setAccessible(true);  // private 필드 접근을 위해 필요
hiddenField.set(example, "modified");
System.out.println(hiddenField.get(example));  // 출력: modified
  • private으로 선언된 필드에 접근하여 수정

Reflection을 적용 예시

1. 디버깅 및 테스팅

private으로 선언된 메서드를 테스트하는데 Reflection 이 이용될 수 있다.

public class SampleClass {
    private String secretMethod() {
        return "Secret Message";
    }
}

public class ReflectionTest {

    void test() {
        SampleClass sample = new SampleClass();

        Method method = SampleClass.class.getDeclaredMethod("secretMethod");

        // 메서드 접근 가능하게 설정
        method.setAccessible(true);

        // 메서드 호출 및 결과 출력
        String result = (String) method.invoke(sample);
		assertThat(result).isEqualTo("Secret Message");
    }
}

2. 동적 프로그래밍

클래스나 메소드가 런타임에 결정되는 경우, Reflection을 사용하여 해당 클래스의 인스턴스를 생성하고 메소드를 호출할 수 있다.

3. 프레임워크 및 라이브러리 개발

Spring Framework

의존성 주입

public class MyService {
    @Autowired
    private MyRepository repository; // Spring이 리플렉션을 사용하여 이 필드에 MyRepository의 인스턴스를 주입
}

Spring은 Reflection을 사용하여 클래스의 의존성을 런타임에 주입한다.
@Autowired 애너테이션이 붙은 필드에 대해 Spring 컨테이너는 해당 타입의 빈(bean)을 찾아 필드에 할당하고,
이 과정에서 리플렉션을 사용하여 필드에 접근하고, 해당 필드에 객체를 할당한다.

AOP
Spring AOP 에서 프록시 패턴을 통해 AOP를 구현하게 되면,
타깃 객체를 감싸는 프록시 객체가 생성되고 이 객체는 타깃 객체와 동일한 인터페이스를 갖는다.

이 때, 프록시 객체의 메서드가 실행되면 Reflection을 통해 타깃 객체의 메서드를 얻어 호출하고,
AOP를 통한 부가기능(로깅, 트랜잭션)을 삽입한다.

Hibernate

ORM

@Entity
public class User {
    @Id
    private Long id;
    private String name; // Hibernate는 리플렉션을 사용하여 이 필드를 데이터베이스 컬럼과 매핑
}

Hibernate는 리플렉션을 사용하여 Java 객체와 데이터베이스 테이블 간의 매핑을 구현한다.
Java 클래스의 필드 이름과 데이터베이스 테이블의 컬럼 이름을 매핑하여 객체를 데이터베이스에 저장하거나,
데이터베이스에서 데이터를 조회하여 객체로 변환하는 과정에서 리플렉션을 활용합니다.

Reflection의 단점

  • 성능 저하 문제
    Reflection은 일반적인 Java 코드보다 실행 속도가 느리다. 따라서 빈번한 사용은 시스템의 전반적인 성능에 영향을 줄 수 있다.
  • 보안 취약점
    Reflection을 사용하면 private 필드나 메소드에 접근할 수 있기 때문에, 보안 관점에서 위험할 수 있다.
  • 접근 제어 문제
    Reflection을 통해 개발자는 일반적으로 접근할 수 없는 클래스의 내부 구현에 접근할 수 있습니다. 이는 캡슐화 원칙을 위반할 수 있다.

0개의 댓글