当我的HttpServlet抛出ServletException时,Tomcat正在记录包含堆栈跟踪的SEVERE消息,尽管它已正确地重定向到web.xml中的另一个HttpServlet。
Tomcat使用stacktrace记录以下消息:
21-Mar-2015 15:24:57.521 SEVERE [http-nio-8080-exec-28] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [MyHttpServlet] in context with path [/HttpServletExceptionHandler] threw exception [CustomException] with root cause CustomException
at MyHttpServlet.doGet(MyHttpServlet.java:20)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
首先,MyHttpServlet抛出一个ServletException包装一个CustomException(Exception的子类)在它的doGet()方法中:
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
throw new ServletException(new CustomException());
}
}
然后,抛出的CustomException被重定向到MyServletExceptionHandler(映射到location' / MyServletExceptionHandler'。这个重定向在web.xml中以下面的方式定义:
<error-page>
<exception-type>CustomException</exception-type>
<location>/MyServletExceptionHandler</location>
</error-page>
最后,MyServletExceptionHandler接收抛出的异常并将其打印出来:
public class MyServletExceptionHandler extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
final Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
System.out.println("MyServletExceptionHandler caught Throwable: " + throwable.toString());
}
}
这会导致预期的&#39; MyServletExceptionHandler被捕获Throwable:CustomException&#39;打印所以这确实有效,但不知何故Tomcat还记录了上面提到的SEVERE消息,包括堆栈跟踪。这会弄乱我的记录。
根据Java Beat的OCEJWCD 6 Mock Exam – 4,上面提到的方法是处理Servlet中的异常处理的正确方法。问题29指出(扰流警报:粗体是正确答案):
如果从java.lang.Exception扩展的业务异常,以下哪项是向客户端发送错误页面的合理方法?
- 抓住异常并使用RequestDispatcher将请求转发到错误页面
- 不捕获异常并在web.xml中定义“错误页面异常”映射
- 捕获异常,将其包装到ServletException中,并在web.xml中定义“错误页面的业务异常”映射
- 捕获异常,将其包装到ServletException中,并在web.xml中定义'ServletException to error-page'映射
- 不做任何事情,servlet容器会自动发送默认错误页面
醇>
第三个答案(标记为正确)明确指出我重新指导例外的方式是一个明智的解决方案。
我在this page上发现了以下引用(来自Coderanch.com的Tom Holloway的10-2-2012)
实际上,ServletException在Web应用程序中无处可去,因此将其显示在主控制台上实际上并不合理,因为它表明应用程序本身并不处理问题。
实际上,Javadocs对ServletException构造函数说了这个:
&#34;使用指定的消息构造一个新的servlet异常。消息可以写入服务器日志和/或为用户显示。&#34;
请注意,它明确表示服务器日志。
服务器可以通过多种方式参与其中。首先,您应该能够在web.xml中定义一个通用异常处理程序,以允许应用程序处理异常,其中该处理程序不仅可以记录到应用程序日志,还可以确定应该执行哪些恢复操作(如果有)采取(更通用的服务器代码不能做的事情)。其次,您可以定义自定义错误页面,在这种情况下,Tomcat将捕获ServletException并分派该页面。但请注意,操作词是页面。与登录屏幕一样,这些页面直接从Tomcat调用,因此无法通过servlet进行路由。换句话说,使用HTML或JSP,而不是Struts或JSF。
但是,最重要的是,抛出ServletExceptions是应用程序设计不良的标志。这意味着某人太懒或太急于妥善处理问题。与此相比,记录错误的位置是次要的。
这句话让我质疑Java Beat的OCEJWCD模拟考试(如上所述)和我自己的解决方案作为良好实践。您认为业务异常应该由另一个Servlet处理吗?如果是这样,你认为Servlet容器(Tomcat)应该记录这些异常的堆栈跟踪吗?如果没有,那么最佳做法是什么?
答案 0 :(得分:1)
您的基本问题似乎是您希望集中处理错误,但不使用导致Tomcat将错误记录为SEVERE
的机制?
由于您从问题中控制所有servlet AFAICT,因此定义一个定义所有错误处理逻辑的抽象基类servlet然后让其余的servlet派生自这个类没有意义吗?
所以你有一个抽象的基础servlet:
public abstract class MyServletBase extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
doGetInternal(req, resp);
} catch (RuntimeException e) {
handleError(e, req, resp);
}
}
protected void handleError(RuntimeException e, HttpServletRequest req, HttpServletResponse resp) {
// Error handling logic goes here
}
protected void doGetInternal(HttpServletRequest req, HttpServletResponse resp);
}
然后你的实际servlet:
public class MyServlet extends MyServletBase {
@Override
protected void doGetInternal(HttpServletRequest req, HttpServlet resp) {
// Actual servlet logic here
}
}
这是一个粗略的草图,没有引用Javadoc,所以可能没有方法签名完美但希望你明白了。
它还具有以下优点:如果您需要向派生的servlet添加额外的错误处理逻辑,并且您不想因为任何原因而想要更改基本servlet,则可以覆盖handleError()
方法在使用Tomcat的异常处理程序机制