JavaはいつUndeclaredThrowableExceptionをスローしますか?
1. 概要
このチュートリアルでは、JavaがUndeclaredThrowableException例外のインスタンスをスローする原因を確認します。
まず、少し理論から始めましょう。 次に、2つの実際の例を使用して、この例外の性質をよりよく理解しようとします。
2. UndeclaredThrowableException
理論的には、宣言されていないチェックされた例外をスローしようとすると、JavaはUndeclaredThrowableExceptionのインスタンスをスローします。 つまり、throws句でチェックされた例外を宣言しませんでしたが、メソッド本体でその例外をスローしました。
Javaコンパイラがコンパイルエラーでこれを防ぐので、これは不可能であると主張する人もいるかもしれません。 たとえば、コンパイルしようとすると、次のようになります。
public void undeclared() {
throw new IOException();
}
Javaコンパイラは次のメッセージで失敗します。
java: unreported exception java.io.IOException; must be caught or declared to be thrown
public void save(Object data) {
// omitted
}
理論について十分に理解できたので、実際の例をいくつか見てみましょう。
3. Java動的プロキシ
最初の例として、 java .util.List インターフェイスのランタイムプロキシを作成し、そのメソッド呼び出しをインターセプトしましょう。 まず、 InvocationHandler インターフェイスを実装し、そこに追加のロジックを配置する必要があります。
public class ExceptionalInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("size".equals(method.getName())) {
throw new SomeCheckedException("Always fails");
}
throw new RuntimeException();
}
}
public class SomeCheckedException extends Exception {
public SomeCheckedException(String message) {
super(message);
}
}
プロキシされたメソッドが次の場合、このプロキシはチェックされた例外をスローします
Javaが両方の状況をどのように処理するかを見てみましょう。 まず、 List.size()メソッドを呼び出します。
ClassLoader classLoader = getClass().getClassLoader();
InvocationHandler invocationHandler = new ExceptionalInvocationHandler();
List<String> proxy = (List<String>) Proxy.newProxyInstance(classLoader,
new Class[] { List.class }, invocationHandler);
assertThatThrownBy(proxy::size)
.isInstanceOf(UndeclaredThrowableException.class)
.hasCauseInstanceOf(SomeCheckedException.class);
上に示したように、 List インターフェースのプロキシを作成し、その上でsizeメソッドを呼び出します。
リストインターフェースで他のメソッドを呼び出す場合:
assertThatThrownBy(proxy::isEmpty).isInstanceOf(RuntimeException.class);
プロキシはチェックされていない例外をスローするため、Javaは例外をそのまま伝播させます。
4. 春の側面
アドバイスされたメソッドがそれらを宣言しなかったときにSpringAspectでチェックされた例外をスローした場合も、同じことが起こります。アノテーションから始めましょう。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ThrowUndeclared {}
次に、このアノテーションが付けられたすべてのメソッドにアドバイスします。
@Aspect
@Component
public class UndeclaredAspect {
@Around("@annotation(undeclared)")
public Object advise(ProceedingJoinPoint pjp, ThrowUndeclared undeclared) throws Throwable {
throw new SomeCheckedException("AOP Checked Exception");
}
}
基本的に、このアドバイスは、そのような例外を宣言していなくても、すべての注釈付きメソッドがチェックされた例外をスローするようにします。 それでは、サービスを作成しましょう。
@Service
public class UndeclaredService {
@ThrowUndeclared
public void doSomething() {}
}
注釈付きメソッドを呼び出すと、JavaはUndeclaredThrowableException例外のインスタンスをスローします。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UndeclaredApplication.class)
public class UndeclaredThrowableExceptionIntegrationTest {
@Autowired private UndeclaredService service;
@Test
public void givenAnAspect_whenCallingAdvisedMethod_thenShouldWrapTheException() {
assertThatThrownBy(service::doSomething)
.isInstanceOf(UndeclaredThrowableException.class)
.hasCauseInstanceOf(SomeCheckedException.class);
}
}
上に示したように、Javaは実際の例外を原因としてカプセル化し、代わりにUndeclaredThrowableException例外をスローします。
5. 結論
このチュートリアルでは、JavaがUndeclaredThrowableException例外のインスタンスをスローする原因を確認しました。
いつものように、すべての例はGitHubでから入手できます。