프로그램을 개발하다 보면 객체의 타입은 모르지만 특정 클래스의 메서드 또는 필드를 호출해야 하는 경우가 있다. 이런 상황에서 Reflection을 사용하여 문제를 해결할 수 있다.
자바는 리플렉션(Reflection)이라는 API를 제공하여 객체의 타입을 몰라도 특정 클래스, 인터페이스의 필드 또는 메서드들을 컴파일 시점이 아닌 런타임(프로그램이 실행 중) 시점에 정보를 추출해낼 수 있고, 호출할 수도 있고, 객체를 생성하거나 변수를 변경할 수도 있다.
주로 프레임워크 또는 라이브러리 개발 시 사용된다.
힙 영역에 로드되어 있는 클래스 타입의 객체를 통해 특정 클래스의 정보를 분석해낸다.
// 1
Class<User> clazz = User.class;
// 2
User user = new User("8", "민지");
Class<? extends User> clazz2 = user.getClass();
// 3
Class<?> clazz3 = Class.forName("org.example.model.User");
public class ReflectionTest {
private static final Logger logger = LoggerFactory.getLogger(ReflectionTest.class);
@Test
void controllerScan() {
Reflections reflections = new Reflections("org.example"); // 파라미터 값의 패키지에서부터 그 하위 패키지를 모두 찾음
Set<Class<?>> beans = new HashSet<>();
beans.addAll(reflections.getTypesAnnotatedWith(Controller.class)); // 파라미터값으로 넘긴 어노테이션이 선언된 클래스를 찾아서 반환
logger.debug("beans: [{}]", beans);
}
}
@Component 어노테이션이 선언된 클래스를 추출해서 출력하는 테스트 코드이다.
Reflections
Reflections 생성자에 넘겨주는 파라미터 값은 클래스를 찾을때의 출발 패키지이다. 파라미터 값이 "org.example"이면 org.example 패키지와 그 하위 패키지를 모두 찾는다. 빈 문자열을 넘기면 자바 classpath에 있는 모든 패키지를 찾는다.
getTypesAnnotatedWith()
이 메소드를 호출할때 어노테이션의 클래스를 파라미터로 넘겨 해당 어노테이션이 붙은 클래스를 찾을 수 있다. getTypesAnnotatedWith(Controller.class)와 같이 호출하면 @Controller 어노테이션이 붙은 클래스를 찾는다.
반환값은 해당 어노테이션이 선언된 클래스 목록이다.
getDeclaredFields()
해당 클래스에서 정의된 변수 목록(private 포함 변수)을 field 클래스 배열 타입으로 리턴한다.
getDeclaredConstructors()
해당 클래스에서 선언된 모든 생성자의 정보를 Constructor 배열 타입으로 리턴한다.
getDeclaredMethod()
해당 클래스에서 선언된 모든 메소드 정보를 리턴한다.