zoukankan      html  css  js  c++  java
  • struts2 异常处理3板斧

    板斧1:找不到action的错误

    在struts.xml中参考如下配置

     1 <struts>
     2 
     3     ...
     4     <package name="default" namespace="/" extends="struts-default">
     5 
     6         ...        
     7 
     8         <default-action-ref name="index" />
     9 
    10         ...
    11 
    12         <action name="index">
    13             <result type="redirectAction">
    14                 <param name="actionName">HelloWorld</param>
    15                 <param name="namespace">/home</param>
    16             </result>
    17         </action>
    18 
    19     </package>
    20 
    21     <include file="struts-home.xml" />
    22 
    23 </struts>
    View Code

    这样,如果输入不存在的.action 路径,会直接重定向到index这个Action上,而index中指定的HelloWorld这个Action,在struts-home.xml中

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE struts PUBLIC
     3         "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
     4         "http://struts.apache.org/dtds/struts-2.0.dtd">
     5 <struts>
     6 
     7     <package name="home" namespace="/home" extends="default">
     8     
     9         <action name="HelloWorld_*" method="{1}" class="HelloWorldAction">
    10             <result>/WEB-INF/views/home/HelloWorld.jsp</result>
    11         </action>
    12 
    13     </package>
    14 </struts>
    View Code

    注:struts.xml中节点出现的顺序,是有严格约定的,如果弄错顺序了,启动时,就会看到类似下面的异常

    org.xml.sax.SAXParseException: The content of element type "package" must match

    "(result-types?,interceptors?,default-interceptor-ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-mappings?,action*)".

    即各节点的顺序为:

    result-types -> interceptors -> default-interceptor-ref -> default-action-ref -> default-class-ref -> global-results -> global-exception-mappings -> action

    板斧2:404/500之类的常规错误

    呃,这个struts2处理不了,得靠web.xml搞定

    1     <error-page>
    2         <error-code>404</error-code>
    3         <location>/WEB-INF/common/error/404.jsp</location>
    4     </error-page>
    5     
    6     <error-page>
    7         <error-code>500</error-code>
    8         <location>/WEB-INF/common/error/500.jsp</location>
    9     </error-page>
    View Code

    板斧3:业务异常/常规(运行)异常

    a) 定义业务异常 (这里简单弄一个土鳖的MyException意思一下)

    package com.cnblogs.yjmyzz.exception;
    
    public class MyException extends Exception {
    
        private static final long serialVersionUID = -8315871537638142775L;
    
        public MyException() {
            super();
        }
    
        public MyException(String message) {
            super(message);
        }
    }
    View Code

    b) Action中,直接向外抛异常即可

     1     public String execute() throws Exception, MyException {
     2 
     3         //testException();
     4 
     5         testMyException();
     6 
     7         return SUCCESS;
     8     }
     9 
    10     /*private void testException() throws Exception {
    11         throw new Exception("normal exception");
    12     }*/
    13 
    14     private void testMyException() throws MyException {
    15         throw new MyException("my exception");
    16     }
    View Code

    c) 定义拦截器,处理异常

    struts2中所有action的方法执行会先经常拦截器,所以拦截器是处理异常的好机机(比如:记录异常到日志文件、转换成友好异常信息)

     1 package com.cnblogs.yjmyzz.Interceptor;
     2 
     3 import org.slf4j.Logger;
     4 import org.slf4j.LoggerFactory;
     5 
     6 import com.cnblogs.yjmyzz.exception.MyException;
     7 import com.opensymphony.xwork2.ActionInvocation;
     8 import com.opensymphony.xwork2.interceptor.*;
     9 
    10 public class ExceptionInterceptor extends AbstractInterceptor {
    11 
    12     private static final long serialVersionUID = -6827886613872084673L;
    13     protected Logger logger = LoggerFactory.getLogger(this.getClass());
    14     protected Logger myexLogger = LoggerFactory.getLogger("my-exception");
    15 
    16     @Override
    17     public String intercept(ActionInvocation ai) throws Exception {
    18         String result = null;
    19         try {
    20             logger.debug("ExceptionInterceptor.intercept() is called!");
    21             result = ai.invoke();
    22         } catch (MyException e) {
    23             // 捕获自定义异常
    24             myexLogger.error(ai.toString(), e);
    25             ai.getStack().push(new ExceptionHolder(e));
    26             result = "error";
    27         } catch (Exception e) {
    28             // 其它异常
    29             logger.error(ai.toString(), e);
    30             ai.getStack().push(new ExceptionHolder(e));
    31             result = "error";
    32         }
    33         return result;
    34     }
    35 
    36 }
    View Code

    解释一下:

    ai.getStack().push(new ExceptionHolder(e)); 这一行的用途是将异常信息放入stack,这样后面的异常处理页面,就能显示异常详细信息

    上面只是演示,将"业务异常MyException"与"常规异常Exception"分开处理,并且用不同的Logger实例来记录,这样就能将"业务异常"与"常规异常"分别记到不同的log文件中,对应的logback.xml参考配置:

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <configuration scan="true" scanPeriod="1800 seconds"
     3     debug="false">
     4 
     5     <property name="USER_HOME" value="logs" />
     6     <property scope="context" name="FILE_NAME" value="test-logback" />
     7 
     8     <timestamp key="byDay" datePattern="yyyy-MM-dd" />
     9 
    10     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    11         <encoder>
    12             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    13             </pattern>
    14         </encoder>
    15     </appender>
    16 
    17     <appender name="file"
    18         class="ch.qos.logback.core.rolling.RollingFileAppender">
    19         <file>${USER_HOME}/${FILE_NAME}.log</file>
    20 
    21         <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    22             <fileNamePattern>${USER_HOME}/${byDay}/${FILE_NAME}-${byDay}-%i.log.zip
    23             </fileNamePattern>
    24             <minIndex>1</minIndex>
    25             <maxIndex>10</maxIndex>
    26         </rollingPolicy>
    27 
    28         <triggeringPolicy
    29             class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    30             <maxFileSize>5MB</maxFileSize>
    31         </triggeringPolicy>
    32         <encoder>
    33             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-4relative [%thread] %-5level
    34                 %logger{150} - %msg%n
    35             </pattern>
    36         </encoder>
    37     </appender>
    38     
    39     
    40     <appender name="exception-file"
    41         class="ch.qos.logback.core.rolling.RollingFileAppender">
    42         <file>${USER_HOME}/${FILE_NAME}_myexception.log</file>
    43 
    44         <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    45             <fileNamePattern>${USER_HOME}/${byDay}/${FILE_NAME}-${byDay}-%i.log.zip
    46             </fileNamePattern>
    47             <minIndex>1</minIndex>
    48             <maxIndex>10</maxIndex>
    49         </rollingPolicy>
    50 
    51         <triggeringPolicy
    52             class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    53             <maxFileSize>5MB</maxFileSize>
    54         </triggeringPolicy>
    55         <encoder>
    56             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-4relative [%thread] %-5level
    57                 %logger{150} - %msg%n
    58             </pattern>
    59         </encoder>
    60     </appender>
    61 
    62     <logger name="com.cnblogs.yjmyzz" level="error" additivity="true">
    63         <appender-ref ref="file" />
    64     </logger>
    65 
    66     <logger name="my-exception" level="error" additivity="true">
    67         <appender-ref ref="exception-file" />
    68     </logger>
    69 
    70     <root level="error">
    71         <appender-ref ref="STDOUT" />
    72     </root>
    73 </configuration>
    View Code

    运行后,会生成二个日志文件,类似下图:(业务日志记录在test-logback_myexception.log中,常规运行异常记录在test-logback.log中)

    tips:如果还有更多的异常类型要处理(比如:SQL异常、Spring异常、网络连接异常等,参考上面的处理)。另:如果把3.b)中Action方法里的testMyException()注释掉,换成testException(),即抛出普通异常,则异常信息将记录到test-logback.log中

    d) struts中拦截器配置,以及全局异常处理页面

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE struts PUBLIC
     3     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
     4     "http://struts.apache.org/dtds/struts-2.3.dtd">
     5 
     6 <struts>
     7 
     8     <constant name="struts.enable.DynamicMethodInvocation" value="false" />
     9     <constant name="struts.devMode" value="true" />
    10 
    11     <package name="default" namespace="/" extends="struts-default">
    12 
    13         <interceptors>
    14             <interceptor name="myinterceptor"
    15                 class="com.cnblogs.yjmyzz.Interceptor.ExceptionInterceptor">
    16             </interceptor>
    17 
    18             <interceptor-stack name="myStack">
    19                 <interceptor-ref name="myinterceptor" />
    20             </interceptor-stack>
    21         </interceptors>
    22 
    23         <default-interceptor-ref name="myStack" />
    24         <default-action-ref name="index" />
    25 
    26         <global-results>
    27             <result name="error">/WEB-INF/common/error.jsp</result>
    28         </global-results>
    29 
    30         <global-exception-mappings>
    31             <exception-mapping exception="java.lang.Exception"
    32                 result="error" />
    33         </global-exception-mappings>
    34 
    35         <action name="index">
    36             <result type="redirectAction">
    37                 <param name="actionName">HelloWorld</param>
    38                 <param name="namespace">/home</param>
    39             </result>
    40         </action>
    41 
    42     </package>
    43 
    44     <include file="struts-home.xml" />
    45     <include file="struts-mytatis.xml" />
    46 
    47 </struts>
    View Code

    解释一下:
    13-21行,注册了自定义的拦截器,如果有更多的拦截器,19行后继续加即可。
    23行,指定了默认的拦截器栈
    26-28行,指定了全局的error返回页面
    30-33行,指定了处理的异常类型

    e) 通用error.jsp 代码 

     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 <%@ taglib prefix="s" uri="/struts-tags" %>
     3 
     4 <html>
     5 <head><title>Simple jsp page</title></head>
     6 <body>
     7     <h3>Exception:</h3>
     8     <s:property value="exception"/>
     9 
    10     <h3>Stack trace:</h3>
    11     <pre>
    12         <s:property value="exceptionStack"/>
    13     </pre>
    14 </body>
    15 </html>
    View Code

    这样运行时,就会显示给用户一个很"低俗但通用"的错误页面:

    显然,直接把底层异常展示给用户是不好的做法(相当于直接告诉别人,今天你底裤的颜色),可以稍微装一下笔,只要改下拦截器:

     1 package com.cnblogs.yjmyzz.Interceptor;
     2 
     3 import org.slf4j.Logger;
     4 import org.slf4j.LoggerFactory;
     5 
     6 import com.cnblogs.yjmyzz.exception.MyException;
     7 import com.opensymphony.xwork2.ActionInvocation;
     8 import com.opensymphony.xwork2.interceptor.*;
     9 
    10 public class ExceptionInterceptor extends AbstractInterceptor {
    11 
    12     private static final long serialVersionUID = -6827886613872084673L;
    13     protected Logger logger = LoggerFactory.getLogger(this.getClass());
    14     protected Logger myexLogger = LoggerFactory.getLogger("my-exception");
    15 
    16     @Override
    17     public String intercept(ActionInvocation ai) throws Exception {
    18         String result = null;
    19         try {
    20             logger.debug("ExceptionInterceptor.intercept() is called!");
    21             result = ai.invoke();
    22         } catch (MyException e) {
    23             // 捕获自定义异常
    24             myexLogger.error(ai.toString(), e);
    25             // 转换成友好异常,并放入stack中
    26             ai.getStack().push(
    27                     new ExceptionHolder(new Exception("业务繁忙,让我喘口气先!")));
    28             result = "error";
    29         } catch (Exception e) {
    30             // 其它异常
    31             logger.error(ai.toString(), e);
    32             // 转换成友好异常,并放入stack中
    33             ai.getStack().push(
    34                     new ExceptionHolder(new Exception("系统太累了,需要休息一下!")));
    35             result = "error";
    36         }
    37         return result;
    38     }
    39 
    40 }
    View Code

    这样,用户看到的信息就变了(当然,实际应用中,下面这个页面,建议请艺术大师美化一下)

    当然,也可以改变拦截器的返回string,比如业务错误,返回"biz-error",定位到业务错误的专用展示页面,常规错误返回"sys-error",返回 另一个专用错误处理页面(对应的struts.xml的全局错误配置也要相应修改)

    小结:

    经过以上处理,常见的异常(错误),比如:404/500、action路径不对、运行异常、业务异常等,即分门别类记录了详细日志(便于日后分析),也转换为友好信息提示给用户,同时还保证了系统健壮性。最后,对于程序员更重要的是,不用手动写try/catch之类的代码了,干活更轻松 (妈妈再也不担心我的异常了)

    附:ajax的统一异常处理,请移步 Struts2、Spring MVC4 框架下的ajax统一异常处理

  • 相关阅读:
    鲸云效总结自动化测试常见问题
    鲸云效解析自动化测试前期的准备很重要
    鲸云效为小程序上线做兼容性审核
    ADO.NET删除和修改
    GIT常用操作命令
    随笔
    你应该知道的计算机延迟数字
    centos安装dotnet-sdk-3.1出现no package问题
    Linux CentOS7.9安装Docker
    CentOS6.8配置IP
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/struts2-exception-handle.html
Copyright © 2011-2022 走看看