Kor*_*out 5 java tomcat servlets exception servletexception
当我的 HttpServlet 抛出 ServletException 时,Tomcat 会记录一条包含堆栈跟踪的 SEVERE 消息,尽管它已正确重定向到 web.xml 中的另一个 HttpServlet。
\nTomcat 使用 stacktrace 记录以下消息:
\n21-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\nat MyHttpServlet.doGet(MyHttpServlet.java:20)\nat javax.servlet.http.HttpServlet.service(HttpServlet.java:618)\nat javax.servlet.http.HttpServlet.service(HttpServlet.java:725)\nat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)\nat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)\nat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\nat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)\nat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)\nat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)\nat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)\nat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)\nat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)\nat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)\nat org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)\nat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)\nat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)\nat org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)\nat org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)\nat org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)\nat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)\nat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)\nat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\nat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\nat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\nat java.lang.Thread.run(Thread.java:745)\n
Run Code Online (Sandbox Code Playgroud)\n首先,MyHttpServlet 抛出一个 ServletException,在它的 doGet() 方法中包装了一个 CustomException(Exception 的子类):
\npublic class MyHttpServlet extends HttpServlet {\n\n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n super.doGet(req, resp);\n throw new ServletException(new CustomException());\n }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n然后,抛出的 CustomException 被重定向到 MyServletExceptionHandler (映射到位置 \'/MyServletExceptionHandler\'。此重定向在 web.xml 中按以下方式定义:
\n<error-page>\n <exception-type>CustomException</exception-type>\n <location>/MyServletExceptionHandler</location>\n</error-page>\n
Run Code Online (Sandbox Code Playgroud)\n最后,MyServletExceptionHandler接收抛出的异常并打印它:
\npublic class MyServletExceptionHandler extends HttpServlet {\n\n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n super.doGet(req, resp);\n final Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");\n System.out.println("MyServletExceptionHandler caught Throwable: " + throwable.toString());\n }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n这会导致预期的“MyServletExceptionHandler catch Throwable: CustomException”打印,因此这确实有效,但不知何故,Tomcat 还记录了上面提到的 SEVERE 消息,包括该堆栈跟踪。这搞乱了我的日志记录。
\n根据 Java Beat 的OCEJWCD 6 模拟考试 \xe2\x80\x93 4上述方法是在 Servlet 中处理异常处理的正确方法。问题 29 指出(剧透警告:粗体是正确答案):
\n\n\n在出现从 java.lang.Exception 扩展的业务异常时,以下哪项是向客户端发送错误页面的明智方法?
\n\n
\n- 捕获异常并使用RequestDispatcher将请求转发到错误页面
\n- Don\xe2\x80\x99t 捕获异常并在 web.xml 中将 \xe2\x80\x98Exception 定义为 error-page\xe2\x80\x99 映射
\n- 捕获异常,将其包装到 ServletException 中,并在 web.xml 中定义 \xe2\x80\x98business 异常到 error-page\xe2\x80\x99 映射
\n- 捕获异常,将其包装到 ServletException 中,并在 web.xml 中定义 \xe2\x80\x98ServletException 到 error-page\xe2\x80\x99 映射
\n- Don\xe2\x80\x99t 不执行任何操作,servlet 容器会自动发送默认错误页面
\n
第三个答案(标记为正确)清楚地表明我重定向异常的方式是一个明智的解决方案。
\n我在此页面上找到了以下引用(来自 CodeRanch.com 的 Tom Holloway,2012 年 10 月 2 日)
\n\n\n实际上,ServletException 在 Web 应用程序中无处可去,因此让它出现在主控制台上并不是那么不合理,因为它表明应用程序本身没有处理问题。
\n事实上,Javadoc 关于 ServletException 构造函数是这样说的:
\n\n\n“使用指定的消息构造一个新的 servlet 异常。该消息可以写入服务器日志和/或向用户显示。”
\n请注意,它明确表示服务器日志。
\n服务器可以通过多种方式参与进来。首先,您应该能够在 web.xml 中定义一个通用异常处理程序,以允许应用程序处理异常,该处理程序不仅可以记录到应用程序日志,还可以确定应该采取什么恢复操作(如果有)采取(更通用的服务器代码无法做到的事情)。其次,您可以定义自定义错误页面,在这种情况下,Tomcat 将捕获 ServletException 并分派该页面。但请注意,操作词是页面。与登录屏幕一样,这些页面是直接从 Tomcat 调用的,因此无法通过 servlet 进行路由。换句话说,使用 HTML 或 JSP,而不是 Struts 或 JSF。
\n但最重要的是,抛出 ServletExceptions 是应用程序设计不良的标志。这意味着某人太懒或太匆忙而无法正确处理问题。与此相比,记录错误的位置是次要的。
\n
这个说法让我对 Java Beat 的 OCEJWCD 模拟考试(上面提到的)和我自己的解决方案作为良好的实践提出质疑。您认为业务异常应该由另一个 Servlet 处理吗?如果是这样,您认为 Servlet 容器(Tomcat)是否应该记录这些异常的堆栈跟踪?如果不是,那么最佳实践是什么?
\n看来您的基本问题是您想要集中错误处理,但不使用导致 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);
}
Run Code Online (Sandbox Code Playgroud)
然后是你的实际 servlet:
public class MyServlet extends MyServletBase {
@Override
protected void doGetInternal(HttpServletRequest req, HttpServlet resp) {
// Actual servlet logic here
}
}
Run Code Online (Sandbox Code Playgroud)
这是我脑海中的粗略草图,没有参考 Javadoc,因此可能没有完美的方法签名,但希望您能理解。
它还有一个优点是,如果您需要向派生 servlet 添加额外的错误处理逻辑,并且您不想出于任何原因更改基本 servlet,您可以覆盖该方法,handleError()
但在以下情况下您无法轻松做到这一点:使用Tomcat的异常处理机制
归档时间: |
|
查看次数: |
4053 次 |
最近记录: |