Struts(十二):异常处理:exception-mapping元素

  • 配置当前action的声明异常处理

1、exception-mapping元素中有2个属性
  exception:指定需要捕获的异常类型
  result:指定一个响应结果,该结果将在捕获到异常时被执行。即可以来自当前action的声明,也可以来自global-results声明。

2、可以在视图上通过<s:property>标签显示异常消息。

 基于上几个章节的例子开始往下写,修改save函数:

    public String save(){     
        System.out.println("save");        
        
        int val=10/0;
        
        return "success";
    }

此时访问http://localhost:8080/Struts_01/,输入信息点击“提交”按钮跳转到details.jsp,当访问save函数时,就跑出了异常信息:

此时,我们发现异常信息并没有被合理的处理掉,比如出错了,我们让页面跳转到error.jsp该多好,其实struts2中通过配置struts.xml是可以实现的。

修改struts.xml:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
    <!-- 
    <constant name="struts.action.extension" value="action" /> 
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    -->
    <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
    <constant name="struts.devMode" value="false" />

    <package name="default" namespace="/" extends="struts-default">
        <action name="product-save" class="com.dx.struts2.valuestack.Product" method="save">
            <exception-mapping result="error" exception="java.lang.ArithmeticException"></exception-mapping>
            <result name="error">/error.jsp</result>    
            <result>/details.jsp</result>
        </action>
    </package>
    <!-- Add packages here -->
</struts>
 

添加在WebContent下添加error.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
</head>
<body>
  error
</body>
</html>

重启服务,重新访问页面,并提交参数,发现页面自动跳转到了error.jsp:

如果光跳转过来不太完美,如果可以显示错误信息不是更好,实际上通过EL或者s:property标签是可以实现的,修改error.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    error<br/>    
    <s:debug/>
    <s:property value="exception" /><br/>
    ${exception}<br/>
    ${exception.message}<br/>
    <s:property value="exception.message"/><br/>
    <s:property value="exceptionStack"/><br/>
    ${exceptionStack}
    
</body>
</html>

显示信息:
s:debug第一个元素类型为:com.opensymphony.xwork2.interceptor.ExceptionHolder

元素包含两个属性:exception和exceptionStack

 

  • 可以通过global-exception-mappings元素为应用程序提供一个全局性的异常捕获映射。

  1、但在global-exception-mappings元素下声明任何exception-mapping元素只能引用在global-results元素下声明的某个result元素。

 只需要修改struts.xml即可,其他不需要变动:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
    <!-- 
    <constant name="struts.action.extension" value="action" /> 
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    -->
    <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
    <constant name="struts.devMode" value="false" />

    <package name="default" namespace="/" extends="struts-default">
        <global-results>
            <result name="error">/error.jsp</result>
        </global-results>
        <global-exception-mappings>
            <exception-mapping result="error" exception="java.lang.ArithmeticException"></exception-mapping>
        </global-exception-mappings>
        <action name="product-save" class="com.dx.struts2.valuestack.Product" method="save">
            <!-- 
            <exception-mapping result="error" exception="java.lang.ArithmeticException"></exception-mapping>
            <result name="error">/error.jsp</result>    
            -->
            <result>/details.jsp</result>
        </action>
    </package>
    <!-- Add packages here -->
</struts>

重新访问即可,其他内容不影响显示。

  • 声明式异常处理机制由ExceptionMappingInterceptor拦截器负责处理。

  1、当某个exception-mapping元素声明的异常被捕获到是,ExceptionMappingInterceptor拦截器就会向ValueStack中添加两个对象:

    exception:表示被捕获异常的Exception对象
    exceptionStack:包含着被捕获异常的栈

  2、什么地方注册这个拦截器?

struts-core.jar 下的struts-default.xml中<default-interceptor-ref name="defaultStack"/>

<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>

 com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor怎么向ValueStack中写入异常信息?

package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Matthew E. Porter (matthew dot porter at metissian dot com) 
 * @author Claus Ibsen
 */
public class ExceptionMappingInterceptor extends AbstractInterceptor {
    
    protected static final Logger LOG = LoggerFactory.getLogger(ExceptionMappingInterceptor.class);

    protected Logger categoryLogger;
    protected boolean logEnabled = false;
    protected String logCategory;
    protected String logLevel;
    

    public boolean isLogEnabled() {
        return logEnabled;
    }

    public void setLogEnabled(boolean logEnabled) {
        this.logEnabled = logEnabled;
    }

    public String getLogCategory() {
        return logCategory;
    }

    public void setLogCategory(String logCatgory) {
        this.logCategory = logCatgory;
    }

    public String getLogLevel() {
        return logLevel;
    }

    public void setLogLevel(String logLevel) {
        this.logLevel = logLevel;
    }

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        String result;

        try {
            result = invocation.invoke();
        } catch (Exception e) {
            if (isLogEnabled()) {
                handleLogging(e);
            }
            List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
            ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
            if (mappingConfig != null && mappingConfig.getResult()!=null) {
                Map parameterMap = mappingConfig.getParams();
                // create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
                invocation.getInvocationContext().setParameters(new HashMap<String, Object>(parameterMap));
                result = mappingConfig.getResult();
                publishException(invocation, new ExceptionHolder(e));
            } else {
                throw e;
            }
        }

        return result;
    }

    /**
     * Handles the logging of the exception.
     * 
     * @param e  the exception to log.
     */
    protected void handleLogging(Exception e) {
        if (logCategory != null) {
            if (categoryLogger == null) {
                // init category logger
                categoryLogger = LoggerFactory.getLogger(logCategory);
            }
            doLog(categoryLogger, e);
        } else {
            doLog(LOG, e);
        }
    }
    
    /**
     * Performs the actual logging.
     * 
     * @param logger  the provided logger to use.
     * @param e  the exception to log.
     */
    protected void doLog(Logger logger, Exception e) {
        if (logLevel == null) {
            logger.debug(e.getMessage(), e);
            return;
        }
        
        if ("trace".equalsIgnoreCase(logLevel)) {
            logger.trace(e.getMessage(), e);
        } else if ("debug".equalsIgnoreCase(logLevel)) {
            logger.debug(e.getMessage(), e);
        } else if ("info".equalsIgnoreCase(logLevel)) {
            logger.info(e.getMessage(), e);
        } else if ("warn".equalsIgnoreCase(logLevel)) {
            logger.warn(e.getMessage(), e);
        } else if ("error".equalsIgnoreCase(logLevel)) {
            logger.error(e.getMessage(), e);
        } else if ("fatal".equalsIgnoreCase(logLevel)) {
            logger.fatal(e.getMessage(), e);
        } else {
            throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
        }
    }

    /**
     * @deprecated since 2.3.15 please use #findMappingFromExceptions directly instead
     */
    protected String findResultFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) {
        ExceptionMappingConfig result = findMappingFromExceptions(exceptionMappings, t);
        return result==null?null:result.getResult();
    }

    /**
     * Try to find appropriate {@link ExceptionMappingConfig} based on provided Throwable
     *
     * @param exceptionMappings list of defined exception mappings
     * @param t caught exception
     * @return appropriate mapping or null
     */
    protected ExceptionMappingConfig findMappingFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) {
        ExceptionMappingConfig config = null;
        // Check for specific exception mappings.
        if (exceptionMappings != null) {
            int deepest = Integer.MAX_VALUE;
            for (Object exceptionMapping : exceptionMappings) {
                ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) exceptionMapping;
                int depth = getDepth(exceptionMappingConfig.getExceptionClassName(), t);
                if (depth >= 0 && depth < deepest) {
                    deepest = depth;
                    config = exceptionMappingConfig;
                }
            }
        }
        return config;
    }

    /**
     * Return the depth to the superclass matching. 0 means ex matches exactly. Returns -1 if there's no match.
     * Otherwise, returns depth. Lowest depth wins.
     *
     * @param exceptionMapping  the mapping classname
     * @param t  the cause
     * @return the depth, if not found -1 is returned.
     */
    public int getDepth(String exceptionMapping, Throwable t) {
        return getDepth(exceptionMapping, t.getClass(), 0);
    }

    private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
        if (exceptionClass.getName().contains(exceptionMapping)) {
            // Found it!
            return depth;
        }
        // If we've gone as far as we can go and haven't found it...
        if (exceptionClass.equals(Throwable.class)) {
            return -1;
        }
        return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
    }

    /**
     * Default implementation to handle ExceptionHolder publishing. Pushes given ExceptionHolder on the stack.
     * Subclasses may override this to customize publishing.
     *
     * @param invocation The invocation to publish Exception for.
     * @param exceptionHolder The exceptionHolder wrapping the Exception to publish.
     */
    protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {
        invocation.getStack().push(exceptionHolder);
    }

}

 

posted @ 2017-03-12 22:22  cctext  阅读(941)  评论(0编辑  收藏  举报