리플렉션(Reflection)

hun·2024년 8월 30일

Java

목록 보기
4/5

자바 런타임 환경에서 class를 동적으로 불러와 처리해야하는 경우가 있다.

런타임 환경에서 클래스의 정보에 접근할 수 있게 해주는 자바 기법을 Reflection API 라고 부른다.

자세히 설명하기 전 대표적으로 어디에 사용하는지 예시부터 보자.

1. Spring 및 Hibernate 같은 프레임워크는 리플렉션을 이용해 객체의 속성이나 메소드를 자동으로 주입하거나 설정한다.

@Component
public class RestTemplateConfig {
	~~
}

@Component
public class WebService {
	
    private RestTemplateConfig restTemplate;
    
	@Autowired
    public WebService(RestTemplateConfig restTemplate){
    	this.restTemplate = restTemplate;
    }
}

Spring 개발을 한번이라도 사용한 사람이라면 의존성 주입을 해봤을거다.

의존성 주입할때 어떻게 리플렉션이 사용되는지 보자.

  1. 스프링은 클래스에서 @Autowired가 붙은 생성자나 필드를 확인, 리플렉션을 통해 클래스의 생성자를 검사한 뒤, 생성자에 @Autowired가 붙어 있는지 확인

  2. 스프링은 리플렉션을 통해 클래스 생성자의 파라미터 타입을 확인

  3. 스프링은 등록된 빈 목록에서 적절한 빈 찾기

  4. 리플렉션으로 객체 생성 및 주입

1.
@Autowired
public webService(RestTemplateConfig restTemplate){...}

2.
//우리가 원하는 생성자는 RestTemplateConfig 객체를 Parameter로 받는 생성자이기 때문에
//RestTemplateConfigclass를 넘겨준다
Constructor<?> constructor = WebService.class.getConstructor(RestTemplateConfig.class);

3.
RestTemplateConfig restTemplateConfig = (RestTemplateConfig) applicationContext.getBean(RestTemplateConfig.class);

4. 
//이 과정에서 리플렉션을 사용해 WebService 인스턴스를 생성하고, 의존성을 주입
WebService webService =  constructor.newInstance(restTemplateConfig);

2. 외부 라이브러리에서 사용 예

객체를 Json으로 직렬화하거나 Json을 객체로 역직렬화 할때 사용하는 Jakson 라이브러리 (예

@Setter
@Getter
@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

public class DeserializationExample {
    public static void main(String[] args) {
        try {
            // JSON 문자열
            String jsonString = "{\"name\":\"Lim\", \"age\":31}";

            // ObjectMapper를 사용해 JSON 문자열을 User 객체로 역직렬화
            ObjectMapper objectMapper = new ObjectMapper();
            User user = objectMapper.readValue(jsonString, User.class);

            // 결과 출력
            System.out.println("Name: " + user.getName());
            System.out.println("Age: " + user.getAge());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ReflectionExample{
	// User 클래스 객체 생성
    User user = User.class.getDeclaredConstructor().newInstance();

	// 리플렉션을 통해 필드에 값 주입
    Field nameField = User.class.getDeclaredField("name");
    nameField.setAccessible(true); // private 필드에 접근 허용
    nameField.set(user, name);

	Field ageField = User.class.getDeclaredField("age");
    ageField.setAccessible(true);
    ageField.set(user, age);

}

이런식으로 알게 모르게 우리는 리플렉션을 많이 사용하고 있었다.

위의 예처럼 런타임에서 동적으로 클래스의 정보를 가져와 사용할 수 있는 이유는 Class 클래스 객체가 존재 하기 때문이다. 클래스명이 Class인 것이다

'Class' 클래스의 동작 원리
자바 프로그래밍이 실행되면 javac 컴파일을 통해 .java -> .class(JVM에서 읽을 수 있는 바이트 코드) 생성 후 Class Loading을 통해 메모리에 로드 한다.

클래스가 메모리에 로드되면, JVM은 해당 클래스에 대한 Class 객체를 생성, 이 Class 객체는 클래스의 모든 메타데이터(예: 메서드, 필드, 생성자, 상속 관계 등)를 포함하고 있다.

Java 8에서 도입된 Metaspace는 클래스 메타데이터를 저장하는 영역이다. 이는 Java 7 이전의 PermGen을 대체한 공간이다.

텍스트크기 조정: 메타스페이스는 PermGen과 달리 크기가 고정되지 않으며, 운영 체제의 가용 메모리를 사용해 필요에 따라 확장할 수 있다. 기본 크기 제한이 없기 때문에 메모리 부족 문제가 발생할 가능성이 줄어든다.

내용: 클래스의 메타데이터, 메서드 정보, 상수 풀, 클래스의 구조 정보 등이 저장. 이곳에 저장된 클래스 메타데이터는 클래스가 언로드될 때까지 유지.

profile
짧더라도 확실한 기록

0개의 댓글