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统一异常处理

  • 相关阅读:
    第4月第1天 makefile automake
    第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash
    第3月第27天 uitableviewcell复用
    learning uboot fstype command
    learning uboot part command
    linux command dialog
    linux command curl and sha256sum implement download verification package
    learning shell script prompt to run with superuser privileges (4)
    learning shell get script absolute path (3)
    learning shell args handing key=value example (2)
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/struts2-exception-handle.html
Copyright © 2011-2022 走看看