거울속에 비춰지는 내 모습으로 나를 보듯이 자바에서도 비슷한 개념이 있습니다.
자바에서는 실제 Class를 사용하는 것이 아닌 JVM 메모리 영역에 올라와있는 데이터를 사용하는 기술이 있습니다.
- java의 소스코드를 컴파일한다.
- 런타임시 컴파일된 class의 바이트 코드들을 클래스 로더를 통해 JVM의 메모리 영역으로 옮깁니다.
- 옮겨진 Class, 필드, Object, 메소드등을 꺼내서 사용한다.
그래서 public 생성자가 존재 하지 않는 거고 public Class 객체는 JVM에 의해 자동으로 생성됩니다.
이런 과정을 통해서 리플렉션을 통해서 아래와 같은 장점을 얻을 수 있어요
- 클래스에 붙은 어노테이션 조회
- 클래스 생성자 조회
- 클래스 필드 조회
- 클래스 메소드 조회
- 부모 클래스, 인터페이스 조회
그럼 이런 기능은 어떻게 사용할 수 있는 걸까요?
먼저 자동으로 생성된 클래스 객체를 가져오는 방법을 알아볼까요?
클래스타입.class
.getClass()
Class.forName("{전체 도메인 네임}")
이 3가지 방법을 통해서 자바안의 클래스 객체를 가져올 수 있는데 메서드는 어떨까요??
1. getMethods
상위 클래스와 상위 인터페이스에서 상속한 메서드를 포함하여 public인 메서드를 모두 가져온다.
2.getDeclaredMethods
접근 제어자와 관계없이 상속한 메서드를 제외하고 직접 클래스에서 선언한 메서드들을 모두 가져온다! ( private도 가능하다는 뜻 )
여기서 getMethod를 사용해도 setAccessable
메서드를 사용하면 접근할 수 있는데요
이 코드를 보면 setAccessable로 private도 접근할 수 있게 해준 다음 함수를 가져오죠?
이렇게 private의 값을 set하여 필드 값을 변경할 수도 있습니다.
기본 생성자를 사용한 경우 모든 경우의 수를 고려하지 않아도 됩니다.
SpringBoot에서도 Entity, Dto,에서도 기본 생성자를 필요로 하는데
이때 기본 생성자로 객체를 생성하고, 필드를 통해 값을 넣어주는 것이 가장 간단한 방법입니다.
그럼 주석의 일부인 어노테이션은 어떻게 보고 로직이 동작하는 걸까요? 이것도 getClass 의 동작방식과 일맥상통합니다.
- 리플렉션을 통해 클래스, 메서드, 필드 , 파라미터 정보를 가져온다.
- 리플렉션의 getAnnotations, getDeclaredAnnotation 등의 메서드를 통해 원하는 어노테이션이 붙어있는지 확인한다.
- 어노테이션이 붙어있다면 원하는 로직을 수행한다.
이 과정을 통해 @Autowired
를 통해 DI하는 과정을 코드로 구현해 볼 수 있습니다.
어떤 어노테이션이 달렸는지 조회해서 Autowired이면
Class객체를 생성 -> setAccessible -> 객체를 등록한다 (Spring 에서는 ApplicationContext 겠죠? ) -> 객체 반환
리플렉션을 이해하니까 Annotation의 동작과정과 Autowired 동작을 동시에 이해가 됐죠?
- 컴파일 시점이 아니라 런타임 시점에서 클래스를 분석한다.
- 컴파일 시점에 제공하는 타입체크 기능을 사용할 수 없다.
- JVM을 최적화 할 수 없기 떄문에 성능 저하 발생
- 코드가 지저분해진다.
- 내부를 노출해서 추상화를 파괴한다. 추상화 불변성을 파괴함.
리플렉션은 아주 제한된 형태로만 사용해야한다.