Это не относится только к Tomcat.Это специфично для Servlet API.Способ определения страницы ошибки указан в главе 9.9.2 Спецификация API сервлета 2.5 .Вот фрагмент релевантности:
SRV.9.9.2 Страницы с ошибками
Если нет error-page
объявления, содержащего exception-type
, подходит с использованием соответствия иерархии классов и исключенияБрошенный является ServletException
или его подклассом, контейнер извлекает упакованное исключение, как определено методом ServletException.getRootCause
.Второй проход выполняется над объявлениями страниц с ошибками, снова предпринимается попытка сопоставления с объявлениями страниц с ошибками, но вместо этого используется переносимое исключение.
Итак, ваш SpecificExceptionA
, вероятно, был заключен в ServletException
и, следовательно, java.lang.Throwable
является ближайшим совпадением на 1-м проходе.Когда вы удалите эту запись, будет выполнен второй проход с исключением в виде обёртки, и, таким образом, ваш SpecificExceptionA
получит совпадение.
Правильный способ определения общей страницы ошибок HTTP 500 - это отобразить ее на error-code
вместо exception-type
:
<error-page>
<exception-type>org.SpecificExceptionA</exception-type>
<location>/WEB-INF/views/error/timedout.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/error/error.jsp</location>
</error-page>
Если это не вариант по какой-то неясной причинеОдин из способов обойти это - создать Filter
, который прослушивает url-pattern
из /*
и делает в основном следующее:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
chain.doFilter(request, response);
} catch (ServletException e) {
Throwable rootCause = e.getRootCause();
if (rootCause instanceof SpecificExceptionA) {
throw (SpecificExceptionA) rootCause;
} else {
throw e;
}
}
}
Он должен расширяться только от RuntimeException
чтобы заставить его работать.