java web 项目异常管理

在实际的j2ee项目中,系统内部不免会出现一些异常,若是把异常听任无论直接打印到浏览器可能会让用户感受莫名其妙,也有可能让某些用户找到破解系统的方法。 java

出来工做一年时间了,我也大概对异常处理有了一些了解,在这呢小弟简单介绍下我的对异常处理的看法,抛砖引玉,但愿各位大神提出宝贵的意见和建议。 程序员

 

就拿spring+struts2+hibernate项目说明:一般一个页面请求到后台之后,首先是到action(也就是所谓mvc的 controller),在action层会调用业务逻辑service,servce层会调用持久层dao获取数据。最后执行结果会汇总到 action,而后经过action控制转发到指定页面,执行流程以下图所示: ajax



 

而这三层其实都有可能发生异常,好比dao层可能会有SQLException,service可能会有 NullPointException,action可能会有IOException,一但发生异常而且程序员未作处理,那么该层不会再往下执行,而是向 调用本身的方法抛出异常,若是dao、service、action层都未处理异常的话,异常信息会抛到服务器,而后服务器会把异常直接打印到页面,结果 就会以下图所示: spring



 

 

其实这种错误对于客户来讲毫无心义,由于他们一般是看不懂这是什么意思的。 数据库

刚学java的时候,咱们处理异常一般两种方法:①直接throws,听任无论;②写try...catch,在catch块中不做任何操做,或者 仅仅printStackTrace()把异常打印到控制台。第一种方法最后就造就了上图的结果;而第二种方法更杯具:页面不报错,可是也不执行用户的请 求,简单的说,其实这就是bug(委婉点:一般是这样)! json

 

那么发生异常到底应该怎么办呢?我想在你们对java异常有必定了解之后,会知道:异常应该在action控制转发以前尽可能处理,同时记录log日志,而后在页面以友好的错误提示告诉用户出错了。你们看下面的代码: 浏览器

Java代码   收藏代码
  1. //建立日志对象  
  2. Log log = LogFactory.getLog(this.getClass());  
  3.   
  4. //action层执行数据添加操做  
  5. public String save(){  
  6.    try{  
  7.          //调用service的save方法  
  8.          service.save(obj);  
  9.    }catch(Exception e){  
  10.          log.error(...);   //记录log日志  
  11.       return "error"; 到指定error页面  
  12.    }  
  13.    return "success";  
  14. }  

 

若是按照上面的方式处理异常之后,咱们用户最后看到的页面可能就会是下面这种形式(我想这种错误提示应该稍微友好点了吧): 服务器



 

 

而后咱们回到刚才处理异常的地方,若是你们积累了一些项目经验之后会发现使用上面那种处理异常的方式可能还不够灵活: mvc

①由于spring把大多数非运行时异常都转换成运行时异常(RuntimeException)最后致使程序员根本不知道什么地方应该进行try...catch操做 app

②每一个方法都重复写try...catch,并且catch块内的代码都很类似,这明显作了不少重复工做并且还很容易出错,同时也加大了单元测试的用例数(项目经理一般喜欢根据代码行来估算UT case)

③发生异常有不少种状况:可能有数据库增删改查错误,多是文件读写错误,等等。用户以为每次发生异常都是“访问过程当中产生错误,请重试”的提示完 全不能说明错误状况,他们但愿让异常信息更详尽些,好比:在执行数据删除时发生错误,这样他们能够更准确地给维护人员提供bug信息。

 

如何解决上面的问题呢?我是这样作的:JDK异常或自定义异常+异常拦截器

struts2拦截器的做用在网上有不少资料,在此再也不赘述,个人异常拦截器原理以下图所示:



 首先个人action类、service类和dao类若是有必要捕获异常,我都会try...catch,catch块内不记录log,一般是抛出一个新异常,而且注明错误信息:

Java代码   收藏代码
  1. //action层执行数据添加操做  
  2. public String save(){  
  3.    try{  
  4.          //调用service的save方法  
  5.          service.save(obj);  
  6.    }catch(Exception e){  
  7.       //你问我为何抛出Runtime异常?由于我懒得在方法后写throws  xx  
  8.       throw new RuntimeException("添加数据时发生错误!",e);  
  9.   }  
  10.    return "success";  
  11. }  

 

 

而后在异常拦截器对异常进行处理,看下面的代码:

Java代码   收藏代码
  1. public String intercept(ActionInvocation actioninvocation) {  
  2.   
  3.         String result = null// Action的返回值  
  4.         try {  
  5.             // 运行被拦截的Action,期间若是发生异常会被catch住  
  6.             result = actioninvocation.invoke();  
  7.             return result;  
  8.         } catch (Exception e) {  
  9.             /** 
  10.              * 处理异常 
  11.              */  
  12.             String errorMsg = "未知错误!";  
  13.             //经过instanceof判断究竟是什么异常类型  
  14.             if (e instanceof BaseException) {  
  15.                 BaseException be = (BaseException) e;  
  16.                 be.printStackTrace(); //开发时打印异常信息,方便调试  
  17.                 if(be.getMessage()!=null||Constants.BLANK.equals(be.getMessage().trim())){  
  18.                     //得到错误信息  
  19.                     errorMsg = be.getMessage().trim();  
  20.                 }  
  21.             } else if(e instanceof RuntimeException){  
  22.                 //未知的运行时异常  
  23.                 RuntimeException re = (RuntimeException)e;  
  24.                 re.printStackTrace();  
  25.             } else{  
  26.                 //未知的严重异常  
  27.                 e.printStackTrace();  
  28.             }  
  29.             //把自定义错误信息  
  30.             HttpServletRequest request = (HttpServletRequest) actioninvocation  
  31.                     .getInvocationContext().get(StrutsStatics.HTTP_REQUEST);  
  32.               
  33.             /** 
  34.              * 发送错误消息到页面 
  35.              */  
  36.             request.setAttribute("errorMsg", errorMsg);  
  37.           
  38.             /** 
  39.              * log4j记录日志 
  40.              */  
  41.             Log log = LogFactory  
  42.                     .getLog(actioninvocation.getAction().getClass());  
  43.             if (e.getCause() != null){  
  44.                 log.error(errorMsg, e);  
  45.             }else{  
  46.                 log.error(errorMsg, e);  
  47.             }  
  48.   
  49.             return "error";  
  50.         }// ...end of catch  
  51.     }  

 须要注意的是:在使用instanceof判断异常类型的时候必定要从子到父依次找,好比BaseException继承与RuntimeException,则必须首先判断是不是BaseException再判断是不是RuntimeException。

 

 

最后在error JSP页面显示具体的错误消息便可:

Java代码   收藏代码
  1. <body>  
  2. <s:if test="%{#request.errorMsg==null}">  
  3.     <p>对不起,系统发生了未知的错误</p>  
  4. </s:if>  
  5. <s:else>  
  6.     <p>${requestScope.errorMsg}</p>  
  7. </s:else>  
  8. </body>  

 

以上方式能够拦截后台代码全部的异常,但若是出现数据库链接异常时不能被捕获的,你们可使用struts2的全局异常处理机制来处理:

Java代码   收藏代码
  1. <global-results>  
  2.     <result name="error" >/Web/common/page/error.jsp</result>  
  3. </global-results>  
  4.            
  5. <global-exception-mappings>  
  6.     <exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>  
  7. </global-exception-mappings>  

 

上面这是一个很简单的异常拦截器,你们可使用自定义异常,那样会更灵活一些。

 

以上异常拦截器可使用其它不少技术替换:好比spring aop,servlet filter等,根据项目实际状况处理。

 

【补充】ajax也能够进行拦截,可是由于ajax属于异步操做,action经过response形式直接把数据返回给ajax回调函数,若是发生异常,ajax是不会执行页面跳转的,因此必须把错误信息返回给回调函数,我针对json数据的ajax是这样作的:

Java代码   收藏代码
  1. /** 
  2.  * 读取文件,获取对应错误消息 
  3.  */  
  4. HttpServletResponse response = (HttpServletResponse)actioninvocation.getInvocationContext().get(StrutsStatics.HTTP_RESPONSE);  
  5. response.setCharacterEncoding(Constants.ENCODING_UTF8);  
  6. /** 
  7.  * 发送错误消息到页面 
  8.  */  
  9. PrintWriter out;  
  10. try {  
  11.     out = response.getWriter();  
  12.     Message msg = new Message(errorMsg);  
  13.     //把异常信息转换成json格式返回给前台  
  14.     out.print(JSONObject.fromObject(msg).toString());  
  15. catch (IOException e1) {  
  16.     throw e;  
  17. }  

 

 

 

以上是我的拙见,勿拍砖,谢谢。

相关文章
相关标签/搜索