아무거나 개발공부/JAVA 기초다지기

JAVA - 리플렉션의 모든 것

코드 살인마 2023. 12. 3. 18:34
728x90

아래 코드는 스프링을 사용한 BookService 코드이다.

@Service
public class BookService {

	@Autowired
	BookRepository bookRepository;

}

질문

  • bookRepository 인스턴스는 어떻게 null이 아닌걸까?
  • 스프링은 어떻게 BookService 인스턴스에 BookRepository 인스턴스를 넣어준 것일까?

 

리플렉션 API - 클래스 정보 조회

리플렉션의 시작은 Class<T> 이다.

Class<T>에 접근하는 방법

  • 모든 클래스를 로딩 한 다음 Class<T>의 인스턴스가 생긴다. “타입.class”로 접근할 수 있다.
  • 모든 인스턴스는 getClass() 메소드를 가지고 있다. “인스턴스.getClass()”로 접근할 수 있다.
  • 클래스를 문자열로 읽어오는 방법

아래 코드는 Class<T>에 접근하는 방법의 3가지 예시이다.

Class<Book> bookClass = Book.class;

Class<? extends Book> aClass = book.getClass();

Class<?> aClass1 = Class.forName("org.example.Book");
  • 클래스패스에 해당 클래스가 없다면 ClassNotFoundException이 발생한다.

Class<T>를 통해 할 수 있는 것

  • 필드 (목록) 가져오기
Book.class.getDeclaredFields()
  • 메소드 (목록) 가져오기
MyBook.class.getDeclaredMethods();
  • 상위 클래스 가져오기 , 인터페이스 (목록) 가져오기 , 애노테이션 가져오기(아래 설명), 생성자 가져오기 등등
  • Modifires 사용 예시
Arrays.stream(Book.class.getDeclaredFields()).forEach(f -> {
            int modifiers = f.getModifiers();
            System.out.println(f);
            System.out.println(Modifier.isPrivate(modifiers)); // Book 클래스 중 private 타입 가져온다.
            System.out.println(Modifier.isStatic(modifiers)); // Book 클래스 중 static 타입 가져온다.
        });

 

애노테이션

애노테이션은 원래 바이트코드를 로딩했을 때 메모리에는 남지 않는다.

근데 만약 메모리에 유지하고 싶다면 @Retention을 이용한다. → 리플렉션으로 조회 가능해진다.

애노테이션

  • @Retention: 해당 애노테이션을 언제까지 유지할 것인가? 소스, 클래스, 런타임
  • @Inherit: 해당 애노테이션을 하위 클래스까지 전달할 것인가?
  • @Target: 어디에 사용할 수 있는가?
@Retention(RetentionPolicy.RUNTIME) // 런타임에서도 해당 에노테이션을 메모리에 저장
@Target({ElementType.TYPE, ElementType.FIELD}) // MyAnnotation 사용할 수 았는 위치 설정 (타입하고, 필드에만 붙일 수 있음)
@Inherited // 하위 클래스에 해당 애노테이션 전파
public @interface MyAnnotation {

    String name() default "ksh";
    int number() default 100;
}

리플렉션

  • getAnnotations(): 상속받은 (@Inherit) 애노테이션까지 조회
  • getDeclaredAnnotations(): 자기 자신에만 붙어있는 애노테이션 조회

 

리플렉션 API - 클래스 정보 수정 또는 실행

먼저 Book 클래스이다.

public class Book {
    private String A = "A";
    public static String B = "B";

    public Book() {
    }

    public Book(String b) {
        B = b;
    }
    public void c() {
        System.out.println("C");
    }

    public int sum(int left, int right) {
        return left + right;
    

정보 수정

Class<?> bookClass = Class.forName("org.example.Book"); // 클래스 로딩
// 기본 생성자 호출
Constructor<?> constructor = bookClass.getConstructor(null);
// 파라미터가 있는 생성자를 통해 새로운 인스턴스 할당
Constructor<?> constructor = bookClass.getConstructor(String.class);
//매개변수 값을 갖는 새로운 인스턴스 생성
Book book = (Book) constructor.newInstance("x");

System.out.println(book);
// B 필드 값을 가져온다.
Field b = Book.class.getDeclaredField("B");
// x
System.out.println(b.get(null));
// AAAAA로 필드 값을 세팅한다.
b.set(null, "AAAAA");
// AAAAA
System.out.println(b.get(null));

실행

Book 클래스에 한 메소드를 실행하는 코드이다.

Class<?> bookClass = Class.forName("org.example.Book"); // 클래스 로딩
// 기본 생성자 호출
Constructor<?> constructor = bookClass.getConstructor();
// 생성자를 통해 새로운 인스턴스 할당
Book book = (Book) constructor.newInstance();
// Book 클래스에 있는 sum 메소드를 불러온다.
Method c = Book.class.getDeclaredMethod("sum",int.class, int.class);
// invoke을 통해 sum 메소드 실행
int invoke = (int) c.invoke(book1,1,2);
// 값은 3이 나온다.
System.out.println(invoke); // 3


리플렉션 정리

리플렉션 API를 간단히 말하면, 클래스에 접근 가능한 API이다.

리플렉션 사용시 주의사항

  • 지나친 사용은 성능 이슈를 야기할 수 있다. 반드시 필요한 경우에만 사용!
  • 컴파일 타입에 확인되지 않고, 런차임 시에만 발생하는 문제를 만들 가능성이 있다.
  • 접근 지시자를 무시할 수 있다.

리플렉션 사용하고 있는 라이브러리

스프링

  • 의존성 주입
  • MVC 뷰에서 넘어온 데이터를 객체에 바인딩 할 때

하이버네이트

  • @Entity 클래스에 Setter가 없다면 리플렉션을 사용한다.

Junit 등등 사용되고 있다.