Programing/JAVA

Java Refrection API 알짜만 빼먹기

리커니 2023. 7. 10. 12:44
반응형

Java의 Refrection API에 대해 알아보도록 하겠습니다.

 

1. Refrection 이란?

리플렉션의 사전적 의미는 '반사' 이며,  Runtime 시점에 동적으로 객체의 정보를 추출하거나

실행할 수 있는 기능을 말합니다.

성능, 디버깅등 많은 단점도 있지만, Spring을 비롯한 여러 유명 라이브러리들에서 사용하는 만큼 강력한 기능이니 사용법을 익혀두도록 합시다.

 

2. Refrection의 단점

2.1 성능 오버헤드

위에서 말씀드린데로, Refrection은 Compile 시점이 아닌 Runtime 시점에 동적으로 객체에 접근하기 때문에 오버헤드가 발생 할 수 있습니다. 오버헤드란 프로그램의 실행시점에 동적으로 코드를 실행해야하는 경우 추가적인 시간, 메모리, 자원등이 사용되는 현상을 말합니다. 10초 걸리는 작업이 20초가 걸릴 수 있다는 것이죠.

 

2.2 컴파일 시점 검증 부재

컴파일러가 정적으로 코드를 검증하지 못하기 때문에 오타나 개발자의 잘못된 사용으로 인한 오류를 컴파일 시점에 검증 할 수 없습니다. IDE에서 알려주는 일반적인 경고나 오류를 확인할 수 없다는 말이죠.

 

2.3 가독성 저하

동적으로 클래스의 메서드나 필드에 접근하기 때문에 코드의 의도와 동작을 이해하는데 어려움이 있을 수 있고, 그러므로 유지보수나 협업에 어려움을 초래할 수 있습니다.

 

2.4 보안 이슈

접근 제어자를 우회하여 private 멤버에 접근하거나 수정할 수 있는 기능을 제공하기 때문에 객체의 상태를 불안정하거나 보안 상의 위험을 가져올 수 있습니다. 그래서 Refrection을 사용할 떄는 보안 검사를 신중하게 수행해야 하며, 안전한 사용법을 준수해야 합니다.

 

3.Refrection의 장점

3.1 동적인 클래스 로딩

Runtime 시점에 동적으로 클래스를 로드하여 코드의 유연성을 제공합니다.

 

3.2 메타데이터 동적 검사

클래스, 필드 메서드, 생성자 등의 메타데이터를 동적으로 검사하여 클래스의 구조를 분석할 수 있습니다. 

 

3.3 동적 객체 생성

클래스의 생성자를 동적으로 호출하여 인스턴스를 생성할 수 있습니다. Spring에서도 Refrection API의 기능을 사용하여 DI (Dependecy Injection)를 구현합니다.

 

3.4 동적 메서드 호출

클래스를 동적으로 로드하여, 해당 클래스의 메서드도 동적으로 호출 할 수 있습니다.

 

Refrection은 위와 같은 장, 단점을 가지고 있습니다. 단점들을 잘 보완해서 사용하면 정말 강력한 기능을 구현할 수 있으니, 적절한 사용방법과 시기를 고려하여 사용하도록 합시다.

 

4. Refrection 샘플 코드

간단한 테스트 코드를 작성해서 Refrection을 활용해 보도록 합시다.

 

4.1 Class의 멤버 확인

/** Object clazz 를 받아 확인*/

/**어노테이션 확인*/
Annotation[] a = clazz.getClass().getDeclaredAnnotations();
for (int i = 0, n = a.length; i < n; i++) {
    System.out.println(a[i].getClass().getName());
}

/**변수명 확인*/
Field[] f = clazz.getClass().getDeclaredFields();
for (int i = 0, n = f.length; i < n; i++) {
    System.out.println(f[i].getName());
}

/**메서드명 확인*/
Method[] m = clazz.getClass().getDeclaredMethods();
for (int i = 0, n = m.length; i < n; i++) {
    System.out.println(m[i].getName());
}

 

4.2 동적으로 인스턴스 생성

Class<T> clazz = classType.getType();
Constructor<T> constructor = clazz.getDeclaredConstructor();
Object newInstance = constructor.newInstance();

 

 

4.3 동적으로 spring bean 의 메서드를 실행 invoke

public class DynamicExcute<P> {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicExcute.class);

    public void excuteMethod(Object clazz, P parameter, String methodName) throws Exception {
        Method method = null;
        try {
            method = clazz.getClass().getDeclaredMethod(methodName, parameter.getClass());
        } catch (NoSuchMethodException | SecurityException e) {
            LOGGER.error("There is no '{}' method in {} class", methodName, clazz.getClass().getName(), e);
            throw e;
        }
        try {
            method.invoke(clazz, parameter);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            LOGGER.error("'{}' method excution error in {} class", methodName, clazz.getClass().getName(), e);
        }
    }
}
try {
    new DynamicExcute<SearchDTO>().excuteMethod(springInstance, parameter, "methodName");
} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

 

반응형