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>

这样,如果输入不存在的.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     @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 }

解释一下:

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>

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

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

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

小结:

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏纯洁的微笑

Guava 源码分析(Cache 原理【二阶段】)

在上文「Guava 源码分析(Cache 原理)」中分析了 Guava Cache 的相关原理。

19210
来自专栏JavaEdge

Bean Validation 介绍及最佳实践关于 Bean ValidationBean Validation 中的 constraint创建一个包含验证逻辑的简单应用(基于 JSP)定制化的 co

31170
来自专栏xdecode

Spring之IOC

IOC 控制反转, 将由程序直接操控的对象调用权交给容器, 通过容器来实现对象的装配和管理. 容器即applicationContext.xml 第一个spri...

20870
来自专栏阿杜的世界

Spring实战3:装配bean的进阶知识主要内容:

在装配bean—依赖注入的本质一文中,我们探讨了Spring的三种管理bean的方式:自动装配、基于JavaConfig、基于XML文件。这篇文字将探讨一些Sp...

10520
来自专栏枕边书

PHP递归创建多级目录(一道面试题的解题过程)

今天看到一道面试题,要写出一个可以创建多级目录的函数: 我的第一个感觉就是用递归创建,具体思路如下: function Directory($dir){   ...

29170
来自专栏决胜机器学习

PHP PDO——单例模式实现数据库操作

PHP PDO——单例模式实现数据库操作 (原创内容,转载请注明来源,谢谢) 一、概述 PDO是PHP访问数据库的轻量、持久的接口,其提供一个...

1.1K80
来自专栏情情说

深入浅出MyBatis:MyBatis解析和运行原理

上一篇介绍了反射和动态代理基础,主要是为本篇文章做个铺垫,反射使配置和灵活性大大提高,可以给很多配置设置参数,动态代理可以在运行时创建代理对象,做一些特殊的处理...

41070
来自专栏Android 研究

Android跨进程通信IPC之4——AndroidIPC基础1

这里强烈建议把前面两篇文章看一遍,因为前面两篇文章对后面大家对android的IPC的理解帮助很大,本片文章主要内容如下

14630
来自专栏程序猿DD

Spring Boot中Web应用的统一异常处理

我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况。Spring Boot提供了一个默认的映射: /error,当处理中抛出异常之后,会转到该请求...

31460
来自专栏Hongten

spring开发_spring中Bean的作用域_singleton_prototype

http://www.cnblogs.com/hongten/gallery/image/112385.html

18520

扫码关注云+社区

领取腾讯云代金券