Meet-Coder-Study / book-effective-java

📔 이펙티브 자바 스터디 저장소

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

아이템 39. 명명 패턴보다 애너테이션을 사용하라

ksy90101 opened this issue · comments

commented

아이템 39-3 마커애너테이션을 처리하는 프로그램에서 리플렉션 처리 과정 중 궁금한 점이 있습니다.
예시는 아래와 같았는데

public class RunTests {
    public static void main(String[] args) throws Exception {
        int tests = 0;
        int passed = 0;
        Class<?> testClass = Class.forName(args[0]); // item39.Sample
        for (Method m : testClass.getDeclaredMethods()) { // 리플렉션으로 사용할 메서드 m1, m2...
            if (m.isAnnotationPresent(Test.class)) { // @Test 어노테이션이 있으면
                tests++;
                try {
                    m.invoke(null); // 실제 메서드 실행
                    passed++; // 성공하면 passed++
                } catch (InvocationTargetException wrappedExc) { // 테스트에서 예외를 던지면 리플렉션 매커니즘이 InvocationTargetException으로 감싸서 다시 던진다.
                    Throwable exc = wrappedExc.getCause();
                    System.out.println(m + " 실패: " + exc);
                } catch (Exception exc) {
                    // 테스트에서 잡지 못한 예외가 있다는것은 @Test 애너테이션을 잘못 사용했다는 뜻.
                    System.out.println("잘못 사용한 @Test: " + m);
                }
            }
        }
        System.out.printf("성공: %d, 실패: %d%n",
                passed, tests - passed);
    }
}

@test 어노테이션을 달아둔 메서드가 실패하면 InvocationTargetException 으로 들어오고, @test 어노테이션을 잘못 사용할 경우에는 Exception 이 발생한다고 되어 있었습니다, 리플렉션에서는 메서드 내부에서 Exception이 발생하면 무조건 InvocationTargetException 으로 던지게 된다고 써있는데, 실제로 익셉션이 NullPointException, IlligalException, ClassCastExcpetion 등이 발생할 요소가 있고 이런 것을 분리해서 롤백/확인을 하고 싶다면 어떻게 해야 하나요?

리플렉션에서는 무조건 InvocationTargetException으로 던지는건 신기하네요 ㅋㅋㅋ
Throwable exc = wrappedExc.getCause();에서 실제 메서드에서 발생한 root cause를 가져올 수 있는걸로 보이네요. 이를 활용하면 되지 않을까 싶어요.

벨덩(?)에서도 Throwable.getCause()를 활용하라고 하네요

commented

@pkch93 아하! 그럼 getCause()로 나온 Exception 이름들을 클래스 이름으로 분기태워서 해야겠네요.
InvocationTargetException에서 getCause()메서드와 함께 getTargetException()도 있길래 실험해보는데 동일하게 Exception 이름이 반환되네요. cause랑 동일한 역할을 하는 친구인 것 같습니다.

https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/InvocationTargetException.html#getTargetException()

네넵 getCause()getTargetException()이랑 동일한 역할을 하네요 ㅋㅋ

    public Throwable getTargetException() {
        return target;
    }

    public Throwable getCause() {
        return target;
    }

스프링도 리플렉션을 사용하고 있어서 어떻게 처리하나 봤는데 getTargetException()으로 실제 발생한 Exception을 가져와서 분기처리하네요.

org.springframework.web.method.support.InvocableHandlerMethod 입니다.

	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}