Skip to content

김현욱, 최현식 리플렉션

HyeonSik Choi edited this page Jul 8, 2024 · 6 revisions

이 미션을 해결하면서 느꼈던 점이나 배운점

  • 김현욱
    • 리플렉션을 직접 사용하는 일은 거의 없었습니다. 프레임워크에서 만들어진 annotation을 사용했으며, 만들어도 간단한 기능의 annotation을 만들어 사용했기 때문에 reflection에 대한 개념을 잊을 수 있었습니다.
    • 이번 미션을 통해 reflection을 리마인드 할 수 있어서 재밌었습니다.
  • 최현식
    • 리플렉션을 사용해본지도 오래되어서 새로웠습니다. 잘모르고 있던 것이 아닌가 라는 생각이 들었습니다.
    • 리플렉션을 이용하면 클래스의 정보, 생성자, 필드, 메서드 정보를 모두 확인할 수 있습니다.
    • 리플렉션을 이용하면 컴파일된 모든 클래스에 접근할 수 있고, 심지어 private 접근제어자도 무시할 수 있어, 잘못 사용하면 큰 책임이 따르겠다고 느꼈습니다.
    • 지금껏 코딩을 하면서 리플렉션을 활용하는 일이 거의 없었습니다. 따라서 '이걸 사용할 일이 많을까?' 하는 생각도 들었습니다.
    • WAS 미션에서 리플렉션과 어노테이션을 이용하면 더 깔끔하게 코드를 관리할 수 있을 것 같습니다 :)

리플렉션이란

  • 리플렉션 API는 컴파일한 클래스 정보를 활용해 동적으로 프로그래밍이 가능하도록 지원하는 API로,
  • 자바에서는 JVM이 클래스 로더를 통해 정보를 불러와 실행시키는 구조이기 때문에, 런타임에 클래스, 함수 등의 정보를 가져올 수 있습니다.

Java Reflection API 활용해 가능한 작업은?

  • Junit에서 @Test 애노테이션이 설정되어 있는 메소드를 단위 테스트로 실행하고 싶다.
  • 현재 실행하는 클래스의 클래스, 필드, 메소드 정보를 알고 싶어요.
  • 인자로 전달하는 클래스의 인스턴스를 생성한 후 메소드를 실행하고 싶어요.
  • Eclipse/Intellij IDEA가 동적으로 setter와 getter 메소드를 만드는 방법이 뭘까요?
  • 데이터베이스에서 조회한 데이터의 칼럼 이름과 자바 클래스의 필드 이름이 같은 경우 자동으로 매핑하고 싶어요.

모든 언어에 리플렉션이 존재할까?

고수준 언어에는 대부분 존재하는 편!

  • C++: 애매함! 리플렉션은 없지만, 템플릿이나 메타정보 정도는 접근할 수 있음
  • C#: 존재함! 닷넷에 가상머신같은게 존재함
  • Python: 존재함! 인터프린터 언어에는 유사하게 모두 존재

어노테이션의 생명주기

어노테이션은 주석과 유사하지만 Retention 명시에 따라 그 생명주기가 달라집니다.

  • SOURCE: 컴파일 시점까지 유지. 소스 코드에서만 존재
    • 예: @Override, @SuppressWarnings
  • CLASS: 컴파일 후 클래스 파일에 유지. 런타임에는 사라짐
    • 예: @Configuration
  • RUNTIME: 컴파일 후 클래스 파일에 유지. 런타임에도 유지
    • 예: @Autowired, @Transactional, @RequestMapping

Byte code 분석

//HelloWorld.class
package next;

public class HelloWorld {
    private int age;
    private String name;
    public HelloWorld(String name,int age){
        this.age = age;
        this.name = name;
    }
}

Compile 한 결과

// class version 55.0 (55)
// access flags 0x21
public class next/HelloWorld {

  // compiled from: HelloWorld.java

  // access flags 0x2
  private I age

  // access flags 0x2
  private Ljava/lang/String; name

  // access flags 0x1
  public <init>(Ljava/lang/String;I)V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 7 L1
    ALOAD 0
    ILOAD 2
    PUTFIELD next/HelloWorld.age : I
   L2
    LINENUMBER 8 L2
    ALOAD 0
    ALOAD 1
    PUTFIELD next/HelloWorld.name : Ljava/lang/String;
   L3
    LINENUMBER 9 L3
    RETURN
   L4
    LOCALVARIABLE this Lnext/HelloWorld; L0 L4 0
    LOCALVARIABLE name Ljava/lang/String; L0 L4 1
    LOCALVARIABLE age I L0 L4 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

클래스 헤더 정보

// class version 55.0 (55)
// access flags 0x21

클래스의 헤더 정보를 나타냅니다.

클래스의 선언부

public class next.HelloWorld {

next.HelloWorld 클래스의 선언부입니다.

필드 정의

// access flags 0x2
private I age
// access flags 0x2
private Ljava/lang/String; name

age 필드와 name 필드를 정의하는 부분입니다.

생성자 정의

// access flags 0x1
public <init>(Ljava/lang/String;I)V

생성자를 정의하는 부분입니다.

  • <init> : 생성자 메서드의 이름입니다. JVM에서 생성자를 나타내는 표기입니다.
  • Ljava/lang/String;I : 생성자의 파라미터 타입을 나타내는 부분입니다. String타입과 int를 인자로 받는 생성자를 정의하고 있습니다.

메서드 본문

L0
 LINENUMBER 6 L0
 ALOAD 0
 INVOKESPECIAL java/lang/Object.<init> ()V
  • L0 : 라벨링 기호로, 이후 명령어들이 참조할 수 있는 코드 블록의 시작점을 나타냅니다.
  • LINENUMBER 6 L0 : 소스 코드의 6번째 줄에 해당하는 명령어 위치를 나타냅니다.
  • ALOAD 0: 로컬 변수 스택에서 인덱스 0에 해당하는 값을 로드합니다. 이는 this를 로드하는 작업입니다.
  • INVOKESPECIAL java/lang/Object.<init> ()V: Object 클래스의 생성자를 호출합니다. 모든 클래스의 최상위 클래스인 Object의 기본 생성자를 호출하여 객체 초기화를 수행합니다.

필드 초기화

L1
 LINENUMBER 7 L1
 ALOAD 0
 ILOAD 2
 PUTFIELD next/HelloWorld.age : I
L2
 LINENUMBER 8 L2
 ALOAD 0
 ALOAD 1
 PUTFIELD next/HelloWorld.name : Ljava/lang/String;
  • L1: 새로운 코드 블록의 시작점을 나타냅니다.
  • LINENUMBER 7 L1: 소스 코드의 7번째 줄에 해당하는 명령어 위치를 나타냅니다.
  • ALOAD 0: 로컬 변수 스택에서 인덱스 0에 해당하는 값을 로드합니다. 여기서는 생성된 객체(this)를 가리킵니다.
  • ILOAD 2: 로컬 변수 스택에서 인덱스 2에 해당하는 값을 로드합니다. 이는 int age 생성자 파라미터를 의미합니다.
  • PUTFIELD next/HelloWorld.age : I: next.HelloWorld 객체의 age 필드에 값을 할당합니다.
  • ALOAD 1: 로컬 변수 스택에서 인덱스 1에 해당하는 값을 로드합니다. 이는 String name 생성자 파라미터를 의미합니다.
  • PUTFIELD next/HelloWorld.name : Ljava/lang/String;: next.HelloWorld 객체의 name 필드에 String 값을 할당합니다.

메서드 종료

L3
 LINENUMBER 9 L3
 RETURN
  • L3: 새로운 코드 블록의 시작점을 나타냅니다.
  • LINENUMBER 9 L3: 소스 코드의 9번째 줄에 해당하는 명령어 위치를 나타냅니다.
  • RETURN: 현재 메서드를 종료하고 호출자에게 반환합니다.

지역 변수 및 스택 크기

L4
 LOCALVARIABLE this Lnext/HelloWorld; L0 L4 0
 LOCALVARIABLE name Ljava/lang/String; L0 L4 1
 LOCALVARIABLE age I L0 L4 2
 MAXSTACK = 2
 MAXLOCALS = 3
  • LOCALVARIABLE: 지역 변수의 선언을 나타냅니다. this, name, age가 각각 0, 1, 2번 인덱스로 선언되어 있음을 보여줍니다.
  • MAXSTACK = 2: 메서드에서 사용할 수 있는 최대 스택 깊이를 나타냅니다. 여기서는 2개의 항목을 사용할 수 있음을 의미합니다.
  • MAXLOCALS = 3: 메서드에서 사용할 수 있는 최대 지역 변수 개수를 나타냅니다. 여기서는 3개의 지역 변수를 사용할 수 있음을 의미합니다.

👼 개인 활동을 기록합시다.

개인 활동 페이지

🧑‍🧑‍🧒‍🧒 그룹 활동을 기록합시다.

그룹 활동 페이지

🎤 미니 세미나

미니 세미나

🤔 기술 블로그 활동

기술 블로그 활동

📚 도서를 추천해주세요

추천 도서 목록

🎸 기타

기타 유용한 학습 링크

Clone this wiki locally