struts2的错误处理

来源:互联网 发布:我的世界0.14.1枪械js 编辑:程序博客网 时间:2024/06/06 14:31

Action执行时是被DefaultActionInvocation对象的invoke方法调用
而在Action执行之前又有一批拦截器
拦截器的调用也是被DefalutActionInvocation对象invoke方法调用
最初调用到DefalutActionInvocation对象invoke方法是由ActionProxy的execute方法调用。

来看一下action执行的部分代码。

protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {try{        if (!methodCalled) {                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);            }            return saveResult(actionConfig, methodResult);        } catch (NoSuchMethodException e) {            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");        } catch (InvocationTargetException e) {            // We try to return the source exception.            Throwable t = e.getTargetException();    }

这里就是捕获了没有这个方法和调用目标异常,没有捕获其他异常,遇到其他异常直接往上抛。
那么我们看看那个方法调用到这个方法

public String invoke() throws Exception {        String profileKey = "invoke: ";        try {            UtilTimerStack.push(profileKey);            if (executed) {                throw new IllegalStateException("Action has already executed");            }            if (interceptors.hasNext()) {                final InterceptorMapping interceptor = interceptors.next();                String interceptorMsg = "interceptor: " + interceptor.getName();                UtilTimerStack.push(interceptorMsg);                try {                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);                            }                finally {                    UtilTimerStack.pop(interceptorMsg);                }            } else {                resultCode = invokeActionOnly();            }            // this is needed because the result will be executed, then control will return to the Interceptor, which will            // return above and flow through again    }

这个invoke方法继续往上抛异常,看看谁调用invoke方法呢?proxy.execute()方法

public String execute() throws Exception {        ActionContext nestedContext = ActionContext.getContext();        ActionContext.setContext(invocation.getInvocationContext());        String retCode = null;        String profileKey = "execute: ";        try {            UtilTimerStack.push(profileKey);            retCode = invocation.invoke();        } finally {            if (cleanupContext) {                ActionContext.setContext(nestedContext);            }            UtilTimerStack.pop(profileKey);        }        return retCode;    }

这里继续往上抛,再看看在那个方法里调用了这个execute()方法呢?

 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);        boolean nullStack = stack == null;        if (nullStack) {            ActionContext ctx = ActionContext.getContext();            if (ctx != null) {                stack = ctx.getValueStack();            }        }        if (stack != null) {            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));        }        String timerKey = "Handling request from Dispatcher";        try {            UtilTimerStack.push(timerKey);            String namespace = mapping.getNamespace();            String name = mapping.getName();            String method = mapping.getMethod();            Configuration config = configurationManager.getConfiguration();            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                    namespace, name, method, extraContext, true, false);            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());            // if the ActionMapping says to go straight to a result, do it!            if (mapping.getResult() != null) {                Result result = mapping.getResult();                result.execute(proxy.getInvocation());            } else {                proxy.execute();            }            // If there was a previous value stack then set it back onto the request            if (!nullStack) {                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);            }        } catch (ConfigurationException e) {            // WW-2874 Only log error if in devMode            if(devMode) {                String reqStr = request.getRequestURI();                if (request.getQueryString() != null) {                    reqStr = reqStr + "?" + request.getQueryString();                }                LOG.error("Could not find action or result\n" + reqStr, e);            }            else {                    if (LOG.isWarnEnabled()) {                LOG.warn("Could not find action or result", e);                    }            }            sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);        } catch (Exception e) {            sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);        } finally {            UtilTimerStack.pop(timerKey);        }    }

在这个dispatcher的serviceAction方法进行了捕获。并用sendError进行了处理

public void sendError(HttpServletRequest request, HttpServletResponse response,            ServletContext ctx, int code, Exception e) {        Boolean devModeOverride = FilterDispatcher.getDevModeOverride();        if (devModeOverride != null ? devModeOverride : devMode) {            response.setContentType("text/html");            try {                FreemarkerManager mgr = getContainer().getInstance(FreemarkerManager.class);                freemarker.template.Configuration config = mgr.getConfiguration(ctx);                Template template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl");                List<Throwable> chain = new ArrayList<Throwable>();                Throwable cur = e;                chain.add(cur);                while ((cur = cur.getCause()) != null) {                    chain.add(cur);                }                HashMap<String,Object> data = new HashMap<String,Object>();                data.put("exception", e);                data.put("unknown", Location.UNKNOWN);                data.put("chain", chain);                data.put("locator", new Locator());                template.process(data, response.getWriter());                response.getWriter().close();            } catch (Exception exp) {                try {                    response.sendError(code, "Unable to show problem report: " + exp);                } catch (IOException ex) {                    // we're already sending an error, not much else we can do if more stuff breaks                }            }        } else {            try {                // WW-1977: Only put errors in the request when code is a 500 error                if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {                    // send a http error response to use the servlet defined error handler                    // make the exception availible to the web.xml defined error page                    request.setAttribute("javax.servlet.error.exception", e);                    // for compatibility                    request.setAttribute("javax.servlet.jsp.jspException", e);                }                // send the error response                response.sendError(code, e.getMessage());            } catch (IOException e1) {                // we're already sending an error, not much else we can do if more stuff breaks            }        }    }

这里获取错误信息模版,将信息放入到模版中进行输出到浏览器

那么我们如何修改错误信息呢?这里我们不必修改,我们要利用好struts2提供的拦截器,这里有一个exception拦截器

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

在默认拦截器栈的栈顶。
那么我们来看看exception拦截器的intercept方法

@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;    }

执行invocation.invoke();时对异常信息进行了捕获捕获好异常信息就执行下面的代码,去找配置文件中的错误exception-error节点,获取result名称,然后执行publishException()方法

protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {        invocation.getStack().push(exceptionHolder);    }

这里将错误信息放入栈顶,然后params过滤器就会错误信息,set到action的成员变量中。
解决方案 :

<package name="struts-global" namespace="/" extends="struts-default">        <global-results>            <result name="errHandler" type="chain">                <param name="actionName">errorProcessor</param>            </result>        </global-results>        <global-exception-mappings>            <exception-mapping exception="java.lang.Exception"                result="errHandler" />        </global-exception-mappings>        <action name="errorProcessor" class="ErrorProcess">            <result>error.jsp</result>        </action>    </package>
0 0