Java Web项目整体异常处理机制

在实际的j2ee项目中,系统内部难免会出现一些异常,如果把异常放任不管直接打印到浏览器可能会让用户感觉莫名其妙,也有可能让某些用户找到破解系统的方法。

  出来工作一年时间了,我也大概对异常处理有了一些了解,在这呢小弟简单介绍下个人对异常处理的见解,抛砖引玉,希望各位大神提出宝贵的意见和建议。

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

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

  其实这种错误对于客户来说毫无意义,因为他们通常是看不懂这是什么意思的。

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

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

//创建日志对象
Log log = LogFactory.getLog(this.getClass());
 
//action层执行数据添加操作
public String save(){
   try{
         //调用service的save方法
         service.save(obj);
   }catch(Exception e){
         log.error(...);   //记录log日志
      return "error"; 到指定error页面
   }
   return "success";
}

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

  然后我们回到刚才处理异常的地方,如果大家积累了一些项目经验以后会发现使用上面那种处理异常的方式可能还不够灵活:

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

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

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

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

  struts2拦截器的作用在网上有很多资料,在此不再赘述,我的异常拦截器原理如下图所示:

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

//action层执行数据添加操作
public String save(){
   try{
         //调用service的save方法
         service.save(obj);
   }catch(Exception e){
      //你问我为什么抛出Runtime异常?因为我懒得在方法后写throws  xx
      throw new RuntimeException("添加数据时发生错误!",e);
  }
   return "success";
}

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

public String intercept(ActionInvocation actioninvocation) {
 
  String result = null; // Action的返回值
  try {
   // 运行被拦截的Action,期间如果发生异常会被catch住
   result = actioninvocation.invoke();
   return result;
  } catch (Exception e) {
   /**
    * 处理异常
    */
   String errorMsg = "未知错误!";
   //通过instanceof判断到底是什么异常类型
   if (e instanceof BaseException) {
    BaseException be = (BaseException) e;
    be.printStackTrace(); //开发时打印异常信息,方便调试
    if(be.getMessage()!=null||Constants.BLANK.equals(be.getMessage().trim())){
     //获得错误信息
     errorMsg = be.getMessage().trim();
    }
   } else if(e instanceof RuntimeException){
    //未知的运行时异常
    RuntimeException re = (RuntimeException)e;
    re.printStackTrace();
   } else{
    //未知的严重异常
    e.printStackTrace();
   }
   //把自定义错误信息
   HttpServletRequest request = (HttpServletRequest) actioninvocation
     .getInvocationContext().get(StrutsStatics.HTTP_REQUEST);
   
   /**
    * 发送错误消息到页面
    */
   request.setAttribute("errorMsg", errorMsg);
  
   /**
    * log4j记录日志
    */
   Log log = LogFactory
     .getLog(actioninvocation.getAction().getClass());
   if (e.getCause() != null){
    log.error(errorMsg, e);
   }else{
    log.error(errorMsg, e);
   }
 
   return "error";
  }// ...end of catch
 }

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

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

<body>
<s:if test="%{#request.errorMsg==null}">
 <p>对不起,系统发生了未知的错误</p>
</s:if>
<s:else>
 <p>${requestScope.errorMsg}</p>
</s:else>
</body>

  以上方式可以拦截后台代码所有的异常,但如果出现数据库连接异常时不能被捕获的,大家可以使用struts2的全局异常处理机制来处理:

<global-results>
 <result name="error" >/Web/common/page/error.jsp</result>
</global-results>
   
<global-exception-mappings>
 <exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>

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

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

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

   /**
    * 读取文件,获取对应错误消息
    */
   HttpServletResponse response = (HttpServletResponse)actioninvocation.getInvocationContext().get(StrutsStatics.HTTP_RESPONSE);
   response.setCharacterEncoding(Constants.ENCODING_UTF8);
   /**
    * 发送错误消息到页面
    */
   PrintWriter out;
   try {
    out = response.getWriter();
    Message msg = new Message(errorMsg);
    //把异常信息转换成json格式返回给前台
    out.print(JSONObject.fromObject(msg).toString());
   } catch (IOException e1) {
    throw e;
   }

====================================分割线================================

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-08-09 23:36:03

Java Web项目整体异常处理机制的相关文章

编写高质量代码改善java程序的151个建议——[110-117]异常及Web项目中异常处理

何为异常处理? 异常处理,英文名为exceptional handling, 是代替日渐衰落的error code方法的新法,提供error code 所未能具体的优势.异常处理分离了接收和处理错误代码.这个功能理清了编程者的思绪,也帮助代码增强了可读性,方便了维护者的阅读和理解. java语言中,异常处理可以确保程序的健壮性,提高系统的可用率.但是java api 提供的异常都是比较低级的,所以有了'提倡异常封装'                                        

Intellij IDEA采用Maven+Spring MVC+Hibernate的架构搭建一个java web项目

原文:Java web 项目搭建 Java web 项目搭建 简介 在上一节java web环境搭建中,我们配置了开发java web项目最基本的环境,现在我们将采用Spring MVC+Spring+Hibernate的架构搭建一个java web项目 Spring MVC+Spring+Hibernate结构介绍如下: Spring MVCSpring MVC的职责是作为web层框架,实现了我们常见的MVC模式 SpringSpring 作为服务层,利用自身特性IOC.AOP,提供数据从DA

java web项目答辩答辩题总结(书本网上语言答辩+自己的语言答辩)

答辩每个人的总分为1.5分.每个人主要问3个问题. 开发流程===>系统架构====>项目模块+功能===>项目得失重定向与转发:?九个隐式对象?get与post的区辨:?jsp有静态包含,动态包含,两者的区辨:?什么是MVC:?web系统架构:? java web项目答辩总结试题 注释:用楷体写的就是用自己组织的话来回答老师的问题. 1 http协议全名和特点 (答辩老师:你对HTTP的是怎么理解的: 答辩同学:首先HTTP是一种超文本传输协议,也是一种无状态的协议.        

Java web项目为什么要单独配置环境变量?

问题描述 Java web项目为什么要单独配置环境变量? Spring的配置文件中的代码如下: class=""org.springframework.beans.factory.config.PropertyPlaceholderConfigurer""> 在运行项目的时候需要配置环境变量之后才能运行,这事为什么呢?希望大神们可以帮我解惑一下,谢谢! 解决方案 这不是配置环境变量 写xxx.properties文件 是为了方便以后项目完成后,在不改变源码的情

Java Web项目经常提示错误

Java Web项目经常提示错误"This project needs to migrate WTP metadata",简单的解决方法就是右击项目然后选择Validate,做完validate后错误就消失了. 出处:http://stevex.blog.51cto.com/4300375/941669

eclipse上搭建maven多模块Java Web项目

1.模块化需求及项目模块说明 手头上有个已上线的系统,但因老板的特殊要求,系统需要不断的修改.还有就是公司市场部不定期地在举行一些微信活动,每一个活动都是周期性的,活动完了这个功能就要在系统中移除. 系统中就有三种模块:已经在系统中正常运行不需要再变更的模块.经常性变更的模块.用完就要移除的活动模块. 所以,我们需要把项目分成了下面几个模块. 简单说明一下: timetable-common是常用工具包存放的模块. wechat-api是微信接口模块,此模块用到了timetable-common

Java Web项目中使用Socket通信多线程、长连接的方法_java

很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听.我们的应用场景是在java项目中,需要外接如一个硬件设备,通过tcp通信,获取设备传上来的数据,并对数据做回应. 先看一下web的监听代码: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class

java web项目利用freeMarker导出word

问题描述 java web项目利用freeMarker导出word 在项目中导出word,利用freeMarker有这样几个问题,求解答 1.如果单独用java项目是可以的,但是放到项目中导出的word打开时会说被程序锁定,然后打开什么内容都没有,查看属性是有字节数. 2.导出的word地址是在后台控制的,如何做到在浏览器弹框选择地址? 解决方案 你是导出word还是导出Excel?导出一般都是调用浏览器本身的下载功能,有的浏览器会让你选择路径,比如360浏览器,有的就不会了,你如果要做一个通用

svn 自动提交java web 项目到tomcat服务器

问题描述 svn 自动提交java web 项目到tomcat服务器 如配置svn提交后自动将javaweb项目更行到自动tomcat服务器中, 情节是这样:团队开发使用svn 在现阶段测试项目中不同的开发人员使用的tomcat服务器,这样造成一些数据不是同步的,现在想配置下让团队开发使用用一个tomcat服务器,这样所操作都是同步的.就想到使用svn自动提交 或者MyEclipse远程使用tomcat 有哪个大神会配置呢? 解决方案 [转]java web项目改名之后tomcat服务器项目自动