[ JAVA ] 리플랙션 ( Reflection )

Wooju Kang ·2025년 5월 22일
post-thumbnail

GIF 출처 : https://www.amigoscode.com/courses/java

🖥 Contents


1 ) 리플랙션 ( Reflection ) 이란?

2 ) 리플랙션 클래스 & 인터페이스

3 ) 리플랙션 특징




1 ) 리플랙션 ( Reflection ) 이란?


: 자바의 리플렉션이란 런타임에 클래스, 메서드, 필드, 생성자 등의 정보를 동적으로 조회하고 조작할 수 있는 기능이다. 리플랙션을 통해 다음과 같은 작업을 수행할 수 있다.

작업 종류예시
클래스 정보 조회클래스 이름, 메서드, 필드 등
객체 생성생성자를 호출하여 인스턴스 생성
메서드 실행이름으로 메서드를 찾아 실행
필드 값 읽기/쓰기private 필드도 접근 가능
어노테이션 정보 조회메타데이터 사용

자바 리플랙션의 경우 모두 java.lang.Class 및. java.lang.reflect패키지에 포함되어있다.

클래스/인터페이스설명
Class<?>클래스 자체를 표현하는 객체
Field클래스의 필드(멤버 변수)를 표현
Method클래스의 메서드를 표현
Constructor클래스의 생성자를 표현
Modifier접근 제어자(public, private 등)를 확인하는 유틸리티



2 ) 리플랙션 클래스 & 인터페이스


Class<?>

: class<?> 클래스는 클래스의 자체를 표현하는 객체이다.

ex )

class Person {

    private static final String name;
    
    private void sayHello(String name) {
    
      System.out.println("Hello My name is " + name);
    }
}


Class<?> classGet = Class.forName("com.example.person"); // 방법1 . forName 메소드로 클래스 가져오기 

Person person = new Person();
Class<?> classGet = person.getClass(); // 방법2. 객체를 생성한 뒤 클래스 가져오기 

Class<Person> classGet = Person.class; // 방법3. 제네릭 타입에 가져올 클래스를 정의하기 

Field

: 클래스의 필드를 표현하는 클래스로 클래스 필드에 접근할 수 있다.

ex )

Field field = classGet.DeclaredField("name"); // name 필드를 가져온다. 
field.setAccessible(true); // private 필드 접근가능 허용
field.set(person,"홍길동"); // 필드 초기화 
String name = (String) field.get(p); // 필드 가져오기 

System.out.println(field.getName());           // 필드 이름
System.out.println(field.getType());           // 필드 타입
System.out.println(Modifier.isPrivate(field.getModifiers())); // 접근 제어자 체크

Method

: 클래스에서 메소드를 표현하는 클래스로 Field 클래스와 비슷하게 메소드에 접근할 수 있다.

ex )

Method method = classGet.DeclaredMethod("sayHello");
method.setAccessible(true);
Object returnValue = method.invoke(person,"우주");

Constructor

: 클래스에서 생성자를 표현하는 클래스이다.

ex )

Constructor<Person> constructor = Person.class.getDeclaredConstructor(String.class); // 생성자 정의 
constructor.setAccessible(true); // private 접근허용 
Person newPerson = constructor.newInstance("우주"); // 새로운 인스턴스 생성 

Modifier

: 클래스, 필드, 메서드, 생성자 등에 적용된 접근 제어자 및 기타 수식자를 정수형 비트 플래그로 표현하고 분석한다.

Modifier 클래스는 상수값에 따라 서로 다른 의미를 지니고 있다. 다음 은 리턴되는 상수값을 정리한 표이다.

Modifier의미상수 값
public공개 접근0x0001
private비공개0x0002
protected상속 클래스에게만 공개0x0004
static클래스 단위(static 영역)0x0008
final변경 불가(상수, 오버라이딩 금지)0x0010
synchronized동기화 (메서드 전용)0x0020
volatile필드가 동기화 없이 읽기 가능0x0040
transient직렬화 제외 (필드 전용)0x0080
native자바 외부 (JNI) 메서드0x0100
abstract구현 없음 (클래스/메서드)0x0400
strictfloat 연산을 엄격하게0x0800
interface인터페이스 타입0x0200
enumenum 타입0x4000

ex )

Field field = Person.class.getDeclaredField("name");
int mod = field.getModifiers();

System.out.println("isPrivate? " + Modifier.isPrivate(mod));   // true
System.out.println("isStatic? " + Modifier.isStatic(mod));     // true
System.out.println("isFinal? " + Modifier.isFinal(mod));       // true
System.out.println("isPublic? " + Modifier.isPublic(mod));     // false



3 ) 리플랙션 특징


자바의 리플랙션은 다음과 같은 장점과 단점을 지니고 있다.

장점


항목설명
유연성코드를 수정하지 않고도 다른 클래스나 메서드를 다룰 수 있음
동적 처리실행 중에 새로운 클래스를 분석하고 동작을 결정할 수 있음
툴 개발에 유리디버깅, 테스트 도구, 직렬화 도구 등에 활용됨

단점


항목설명
성능 저하내부적으로 많은 검사를 거치기 때문에 일반 호출보다 느림
타입 안전성 ↓컴파일러가 타입을 체크하지 못하므로 ClassCastException 가능
캡슐화 파괴private 필드나 메서드도 접근할 수 있음

그럼 자바의 리플랙션은 어떻게 활용될까? POJO 의 관점과 프레임워크의 관점으로 나누어볼 수 있다.

① POJO

  • 객체 구조 분석 (Class, Field, Method 등)
    : 객체 구조를 런타임에 조사하고 디버깅&개발 툴에서 객체 내부의 정보를 출력하도록 한다.

    ex )

for (Field f : obj.getClass().getDeclaredFields()) {
    f.setAccessible(true);
    System.out.println(f.getName() + ": " + f.get(obj));
}

  • 테스트에서의 private 접근
    : setAccessible 메소드를 통해 테스트에서도 private필드나 메소드에 접근할 수 있도록한다.

ex )

Method m = clazz.getDeclaredMethod("internalLogic");
m.setAccessible(true);
m.invoke(obj);

  • 커스텀 어노테이션 생성하기
    : 리플랙션을 통해서 필요한 어노테이션 인터페이스를 생성할 수 있다.

ex )

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
    String value() default "기본 로그";
}
public class TransactionHandler { 

   private Object target;
   
   public TransactionHandler(Object target) {
     
      this.target = target;
   }

   public Object invoke(String methodName, Object... args) throws Exception {
        Method method = target.getClass().getMethod(methodName);
        if (method.isAnnotationPresent(MyTransactional.class)) {
            try {
                beginTransaction();
                Object result = method.invoke(target, args);
                commit();
                return result;
            } catch (Exception e) {
                rollback();
                throw e;
            }
        } else {
            return method.invoke(target, args);
        }
    }
    
}

② 프레임워크


  • DI/IoC 컨테이너
    : Config 클래스 혹은 어노테이션을 통해 객체를 동적으로 생성하고 의존성을 주입한다. 이때 , 리플랙션을 통해 생성자 & 필드 & 메서드에 접근한다.

ex )

Constructor<?> ctor = clazz.getDeclaredConstructor();
Object bean = ctor.newInstance();
Field f = clazz.getDeclaredField("repository");
f.setAccessible(true);
f.set(bean, repositoryInstance);

  • AOP ⭐️
    : 기본적으로 Spring AOP는 자바의 리플랙션을 기반으로 원하는 클래스 or 경로 ( PointCut ) 에 원하는 시점을 설정하여 특정로직 ( Advice ) 을 추가할 수 있다.

    AOP에 대한 자세한 설명은 Spring AOP 포스팅 참조!

ex )

Object result = method.invoke(target, args); // Around advice
  • 테스트 프레임워크 ( JUnit )
    : 테스트 프레임워크에서는 특정 로직을 테스트 하기 위해 스프링의 설정 정보를 불러올 필요가 있다. 이를 위해 리플랙션을 통해 설정 정보를 불러온다.

ex )

Method test = testClass.getDeclaredMethod("shouldRun");
test.setAccessible(true);
test.invoke(testInstance);

profile
배겐드 📡

0개의 댓글