BE/Java

[Java] Reflection?

멍목 2024. 12. 16. 19:55
반응형

Reflection(리플렉션)

  • 클래스의 실체가 아닌 반사된 모습을 이용한 기능.
  • Class 를 이용
  • 리플렉션 기능을 지원하지 않는 언어 : C, C++. Pascal
  • 사용되는 기술 : JPA, Jackson, Mockito, Junit ...
  • 장점 
    • 외부, 사용자 정의 클래스들을 사용할 수 있게 함
    • 접근제어자에 관계없이 접근이 가능하도록 함
  • 단점
    • 컴파일 시점이 아닌, 런타임 시점에 클래스를 분석
      JVM을 최적화할 수 없어 성능저하
    • 마찬가지로, 컴파일 에러 확인 불가
    • 코드 가독성 저하
    • 내부가 노출되어 추상화 파괴 

 

Class

  • 실행중인 자바 어플리케이션의 클래스와 인터페이스의 정보를 가진 클래스
  • 클래스와 인터페이스의 정보를 가지고 있기 때문에, Class는 클래스의 내용을 모두 확인 가능
    (ex, 어노테이션, 생성자, 필드, 메서드, 부모 클래스 등)
  • public 생성자가 존재하지 않고, JVM에 의해 자동으로 생성
  • 메서드에 Declared 가 붙어있는 경우, 접근제어자 관계없이 해당 클래스에서 직접 관여한 부분을 조회
    • getMethods() : public 메서드에 한해서 부모 클래스와 부모 인터페이스에서 상속한 메서드를 포함하여 조회
      getDeclaredMethods() : 접근 제어자 관계없이 상속한 메서드를 제외하고 해당 클래스에서 직접 선언한 메서드를 조

 

Class 사용 방법

Person.java

package com.java8.other.reflection;

public class Person {
    String name;
    int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private void privatePrint() {
        System.out.println("privatePrint : " + toString());
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}

 

1) Class 가져오기

// 1. {클래스 이름}.class
Class<?> clazz1 = Person.class;

// 2. {인스턴스}.getClass()
Person person = new Person("김멍목", 28);
Class<?> clazz2 =  person.getClass();

// 3.Class.forName("클래스 주소")
Class<?> clazz3 = Class.forName("com.java8.other.reflection.Person");

 

 

2) 생성자를 가져와서 인스턴스 만들기

.getConstructor() 를 이용해서 생성자를 가져올 수 있으며, 파라미터에 따라 생성자를 구분 가능

Constructor<?> declaredConstructor1 = clazz2.getDeclaredConstructor();
Constructor<?> declaredConstructor2 = clazz2.getDeclaredConstructor(String.class, int.class);
Object instance = declaredConstructor2.newInstance("멍목2", 7);       // 방금 알아낸 생성자로 인스턴스 생성
System.out.println(instance.toString());

 

 

3) 접근제어자 무시하고 메서드 호출하기

Person person = new Person("김멍목", 28);
Class<?> clazz2 =  person.getClass();

Method method = clazz2.getDeclaredMethod("privatePrint");
method.setAccessible(true);     // 접근제어자 신경쓰지 않겠다.
System.out.println(method.getName());
method.invoke(person);          // 해당 메서드 실행

 

 

※ Entity, Request/Response DTO 에 기본 생성자가 필요한 이유

  • 리플렉션으로 기본 생성자를 가져와 인스턴스를 생성하고, 필드를 가져와서 값을 주입해주는 것이 간단하기 때문
  • 여러 생성자가 있을 경우, 간단하게 파라미터 타입이나 갯수를 신경쓰지 않고 기본 생성자로 객체 생성

 

※ Annotation은 주석인데 작동하는 이유

  • 리플렉션으로 getAnnotation(s)를 통해 해당 클래스에 어노테이션이 있는 지 확인

 

 

Reference

https://youtu.be/67YdHbPZJn4

https://ebabby.tistory.com/4

반응형