2)下载Struts2.3.16的开源包,关联继续调试,终于真相大白:截取一下ExceptionMappingInterceptor中的部分源码如下:
@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; }
protected ExceptionMappingConfig findMappingFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) { <span style="white-space:pre"> </span>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; } 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); }从这个拦截器的源码中可以看到整个流程:先取到配置文件中的异常列表,然后把抛出的异常跟这个列表中的异常作比较,这个逻辑在上一次的debug已经认证了,主要是是匹配的这个段代码
<span style="white-space:pre"> </span>if (exceptionClass.getName().contains(exceptionMapping)) { // Found it! return depth; }首先判断异常类的名字跟我们配置文件中取到的类名是否一致,如果一致ok,如果不一致继续判断
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);这段代码告诉了我们如何判断,直接取我们抛出的异常的上级进行判断,这样循环,直接到最顶级的父级进行判断。显然我们抛出的jasperException不可能出现在这里,从
return getDepth(exceptionMapping, t.getClass(), 0);这段代码已经注定了不能匹配成功了,直接把我们抛出的异常转化为了class,使得我们的其它信息已经丢失。
解决思路:
1.我的第一印象修改源代码- -!后来一想,这个属于下策,可能其它地方我们有注意到,导致我自己的失误,所以想其他方法
2.网络是个好东西,看了一下网上的这方面的资料,倾向于自定义的拦截器,毕竟我调试的源码也是自带的拦截器,为何不自己也定义一个拦截器进行修正呢。
3.修改之后的struts.xml如下:
<interceptors> <span style="white-space:pre"> </span><interceptor name="MyCustomInterceptor" class="cn.com.cottech.cotton.Interceptor.MyCustomInterceptor"></interceptor> <span style="white-space:pre"> </span><!--这里可以配置多个interceptor--> <span style="white-space:pre"> </span><!--拦截器栈--> <span style="white-space:pre"> </span><interceptor-stack name="MyInterceptorStack"> <span style="white-space:pre"> </span><interceptor-ref name="defaultStack" /> <span style="white-space:pre"> </span><interceptor-ref name="MyCustomInterceptor"></interceptor-ref> <span style="white-space:pre"> </span><!--这里也可以配置多个interceptor-ref--> <span style="white-space:pre"> </span></interceptor-stack> <span style="white-space:pre"> </span></interceptors> <span style="white-space:pre"> </span><!-- 全局默认拦截器 ,一个系统中只能一个全局默认--> <span style="white-space:pre"> </span><default-interceptor-ref name="MyInterceptorStack"></default-interceptor-ref> <span style="white-space:pre"> </span><!-- 设置全局的返回结果 --> <span style="white-space:pre"> </span><global-results> <span style="white-space:pre"> </span><result name="login">/login.jsp</result> <span style="white-space:pre"> </span><result name="error">/error.jsp</result> <span style="white-space:pre"> </span><result name="prompt">/prompt-page.jsp</result> <span style="white-space:pre"> </span><result name="no-permission-exception">/no-permission-exception.jsp</result> <span style="white-space:pre"> </span></global-results>里面的一些定义就不解释了,这里注意一下,拦截器的顺序,可以看到有两个拦截器,一个是系统默认的,还有一个自定义的,默认的必须有,它牵扯到struts整个路程的各种处理,比如赋值,request,session,form的值等等。
我的自定义的部分代码如下:
@Override <span style="white-space:pre"> </span>public String intercept(ActionInvocation ac) throws Exception { <span style="white-space:pre"> </span>String result = "error"; <span style="white-space:pre"> </span>try { <span style="white-space:pre"> </span>result = ac.invoke(); <span style="white-space:pre"> </span>} catch (Exception e) { <span style="white-space:pre"> </span>log.debug(e, e); <span style="white-space:pre"> </span>e.printStackTrace(); <span style="white-space:pre"> </span>// 这里可以有多个catch,但要注意顺序问题,用于捕捉不同的异常,进行不同的处理 <span style="white-space:pre"> </span>List<String> exceptionList = new ArrayList<String>(); <span style="white-space:pre"> </span>exceptionList.add(NoPermissionException.class.getName()+",no-permission-exception"); <span style="white-space:pre"> </span>exceptionList.add(NullPointerException.class.getName()+",error"); <span style="white-space:pre"> </span>int deepest = Integer.MAX_VALUE; <span style="white-space:pre"> </span>for(int i=0;i<exceptionList.size();i++){ <span style="white-space:pre"> </span>String[] exception_con = exceptionList.get(i).split(","); <span style="white-space:pre"> </span>String exceptionName = exception_con[0]; <span style="white-space:pre"> </span>if(isContainException(exceptionName, e)){ <span style="white-space:pre"> </span>return exception_con[1]; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>return result; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span> <span style="white-space:pre"> </span> public boolean isContainException(String exceptionMapping, Throwable t) { <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>int depth = 1; <span style="white-space:pre"> </span>if(t.getClass().getName().contains(exceptionMapping)){//直接判断异常的名称是什么 <span style="white-space:pre"> </span>return true; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>Throwable tb = t.getCause(); <span style="white-space:pre"> </span>while(tb!=null){//如果异常名称不符合,那就查看他的起因 <span style="white-space:pre"> </span>if(tb.getMessage().contains(exceptionMapping)){ <span style="white-space:pre"> </span>return true;//if the cause contains exceptionMapping,return depth,else invoke getDepth(String exceptionMapping, Class exceptionClass, int depth) <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>tb = tb.getCause(); <span style="white-space:pre"> </span>if(depth>=Integer.MAX_VALUE){//Prevent loop <span style="white-space:pre"> </span>break; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>++depth; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>return false; //return getDepth(exceptionMapping, t.getClass(), 0); }自定义的拦截器主要功能有两个:
1)打印异常,出现异常说明程序有问题,这个是需要程序员进行跟踪修改的。
2)主要处理我自定义的权限异常,将来可以添加系统的和自定义的其它异常。
备注:这里的异常是写死的,可以参考源码,直接从config配置中取。这里就不修改了
到此这个异常体系基本建立