Struts2异常处理

来源:互联网 发布:那个软件看欧美剧 编辑:程序博客网 时间:2024/06/05 17:14

板斧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>
复制代码

这样,如果输入不存在的.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>
复制代码

注: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>
复制代码

 

板斧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);    }}
复制代码

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     }
复制代码

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     @Override17     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 }
复制代码

解释一下:

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%n13             </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.zip23             </fileNamePattern>24             <minIndex>1</minIndex>25             <maxIndex>10</maxIndex>26         </rollingPolicy>27 28         <triggeringPolicy29             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] %-5level34                 %logger{150} - %msg%n35             </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.zip46             </fileNamePattern>47             <minIndex>1</minIndex>48             <maxIndex>10</maxIndex>49         </rollingPolicy>50 51         <triggeringPolicy52             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] %-5level57                 %logger{150} - %msg%n58             </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>
复制代码

运行后,会生成二个日志文件,类似下图:(业务日志记录在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>
复制代码

解释一下:
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>
复制代码

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

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

复制代码
 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     @Override17     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 }
复制代码

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

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

 

小结:

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


作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

1 0
原创粉丝点击