[우아한테크코스] 9월 8일 TIL

1 minute read

[Java] Reflection

객체를 통해 클래스의 정보를 가지고 오는 프로그램 기법으로 Spring에서 Bean Factory가 사용한다.

Bean Factory는 객체가 호출될 때 객체의 인스턴스를 생성한다.

이렇게 동적으로 컴파일한 클래스의 정보를 분석해 인스턴스를 생성해 다룰 수 있도록 하는 것이 Reflection이다.

  1. 클래스를 바탕으로 Class 객체 찾기

    // 같은 패키지 내에 Question 클래스 있는 경우 데려옴
    final Class<Question> clazz = Question.class;
    
  2. 클래스의 이름을 통해 Class 객체 찾기

    // 같은 패키지 내에 reflection 패키지 안에 Question 클래스 있는 경우 데려옴
    final Class<?> clazz = Class.forName("reflection.Question");
    
  3. 객체로부터 선언된 필드/함수/생성자 찾기

    // Student 객체를 찾아 선언된 필드를 데려옴
    final Object student = new Student();
    // getDeclaredFields 하면 선언된 모든 fields 데려옴
    final Field[] fields = student.getClass().getDeclaredFields(); 
    // getFields 하면 publicFields 데려옴
    final Field[] fields = student.getClass().getFields(); 
    // field 이름 전달하면 해당 필드 가져와 타입 얻기, 값 얻기 가능
    final Field field = questionClass.getDeclaredField("questionId");
       
    final List<String> actualFieldNames = Arrays.stream(fields)
      .map(Field::getName)
      .collect(Collectors.toList());
       
    // Student 객체를 찾아 선언된 함수를 가져옴
    // Object로 만들어 getClass().getDeclaredMethods도 가능
    final Class<?> animalClass = Student.class;
    final Method[] methods = animalClass.getDeclaredMethods();
    final List<String> actualMethods = Arrays.stream(methods)
      .map(Method::getName)
      .collect(Collectors.toList());
       
    // 객체를 찾아 생성자들 가져옴
    final Constructor<?>[] constructors = questionClass.getConstructors();
       
       
    
  4. 여러 생성자를 가진 객체 가져와서 새로운 인스턴스 만들어 생성

    final Class<?> questionClass = Question.class;
    final Constructor<?> firstConstructor = questionClass.getConstructors()[0];
    final Constructor<?> secondConstructor = questionClass.getConstructors()[1];
       
    final Question firstQuestion = (Question) firstConstructor.newInstance("gugu", "제목1", "내용1");
    final Question secondQuestion = (Question) secondConstructor.newInstance(1, "gugu", "제목2", "내용2", Date.valueOf(LocalDate.now()), 0);
    
  5. 객체를 찾아서 하나 만들고 필드 세팅하기

    final Class<?> studentClass = Student.class;
    final Student student = (Student) studentClass.getConstructor().newInstance();
    final Field field = student.getClass().getDeclaredField("age");
    field.setAccessible(true);
    field.set(student, 99);
    
  • 함수 찾아 실행하기

    Class<Junit3Test> clazz = Junit3Test.class;
    Junit3Test junit3Test = clazz.getConstructor().newInstance();
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
      if (method.getName().startsWith("test")) {	//test로 시작하는 함수를 찾아
        method.invoke(junit3Test);	// 실행한다, 클래스를 변수로 전달해야함
      }
    }
    
  • 메서드에서 어노테이션 찾아서 특정 어노테이션 가진 것만 실행

    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
      Annotation[] annotations = method.getAnnotations();
      for (Annotation annotation : annotations) {
        if (annotation.annotationType() == MyTest.class) {
          method.invoke(junit4Test);
        }
      }
    }
    
  • 입력받은 변수의 패키지를 바탕으로 Controller 어노테이션 가진 클래스들 찾기

    Reflections reflections = new Reflections("examples");
    Set<Class<?>> controllers = reflections.getTypesAnnotatedWith(Controller.class);