빡코

리플렉션 API 개념 및 실습 본문

Java/Spring Framework

리플렉션 API 개념 및 실습

chris.djang 2024. 1. 15. 23:05

@Controller 애노테이션이 설정돼 있는 모든 클래스를 찾아서 출력하는 예제를 실습 

 

Reflection
• 힙 영역에 로드돼 있는 클래스 타입의 객체를 통해 필드/메소드/생성자를 접근 제어
자와 상관 없이 사용할 수 있도록 지원하는 API
• 컴파일 시점이 아닌 런타임 시점에 동적으로 특정 클래스의 정보를 추출해낼 수 있는
프로그래밍 기법
• 주로 프레임워크 또는 라이브러리 개발 시 사용됨
• https://www.baeldung.com/reflections-library

 

전체적인 프로젝트 구조 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {

    String value() default "";
    RequestMethod[] method() default {};
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
public enum RequestMethod {
 GET, POST, PUT, DELETE
}
@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(HttpServletRequest request, HttpServletResponse response) {
        return "home";
    }
}
@Service
public class HomeService {
}
package org.example.model;

import java.util.Objects;

public class User {

    private String userId;
    private String name;

    public User(String userId, String name) {
        this.userId = userId;
        this.name = name;
    }

    public boolean equalsUser(User user) {
        return this.equals(user);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(userId, user.userId) && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, name);
    }
}

/**
 * @Controller 애노테이션이 설정돼 있는 모든 클래스를 찾아서 출력한다
 */

public class ReflectionTest {

    private static final Logger logger = LoggerFactory.getLogger(ReflectionTest.class);

    @Test
    void ControllerScan() {

        Set<Class<?>> beans = getTypeAnnotationWith(List.of(Controller.class, Service.class));

        logger.debug("beans:[{}]", beans);

    }

    @Test
    void showClass() {
        Class<User> clazz = User.class;
        logger.debug(clazz.getName());
        //클래스에 선언된 필드 리스트를 가져올 수 있음
        logger.debug("User all declared fields: [{}]", Arrays.stream(clazz.getDeclaredFields()).collect(Collectors.toList()));
        logger.debug("User all declared constructors: [{}]", Arrays.stream(clazz.getConstructors()).collect(Collectors.toList()));
        logger.debug("User all declared methods: [{}]", Arrays.stream(clazz.getMethods()).collect(Collectors.toList()));

    }

    @Test
    void load() throws ClassNotFoundException {
        /**
        * 힙 영역에 로드되어 있는 클래스 타입 객체를 가지고 오는 마지막 세가지 방법
        */
        // 1
        Class<User> clazz = User.class;

        // 2
        User user = new User("servewizard", "크리스");
        Class<? extends User> clazz2 = user.getClass();

        // 3
        Class<?> clazz3 = Class.forName("org.example.model.User");

        logger.debug("clazz: [{}]", clazz);
        logger.debug("clazz2: [{}]", clazz2);
        logger.debug("clazz3: [{}]", clazz3);

        Assertions.assertThat(clazz == clazz2).isTrue();
        Assertions.assertThat(clazz2 == clazz3).isTrue();
        Assertions.assertThat(clazz3 == clazz).isTrue();



    }

    private Set<Class<?>> getTypeAnnotationWith(List<Class<? extends Annotation>> annotations) {
        Reflections reflections = new Reflections("org.example");//org.example 하위 패키지 대상으로 등록

        Set<Class<?>> beans = new HashSet<>();
        annotations.forEach(annotation -> beans.addAll(reflections.getTypesAnnotatedWith(annotation)));
        //beans.addAll(reflections.getTypesAnnotatedWith(Controller.class));//@Controller 어노테이션 붙어있는 클래스를 찾아서 담는다
        //beans.addAll(reflections.getTypesAnnotatedWith(Service.class));//@Controller 어노테이션 붙어있는 클래스를 찾아서 담는다
        return beans;
    }


}

 

Git 

https://github.com/chrisjang-8921/reflectionAPI-practice.git