Java异常机制是为了对程序中可能出现的已知错误进行捕获,并进行相应处理。从是否反馈给用户来看,存在两类异常: java
系统异常:这类异常由系统自己的低级异常引发,例如数据库链接失败、内存溢出、空指针异常等等,这类异常不须要出如今前台,由于用户看不懂也没有必要看到这些异常信息。这类异常须要在日志中进行完整记录以供往后开发人员进行查看分析。 sql
应用异常:即自定义异常,这类异常须要经过前台反馈给用户,友好提示用户当前操做异常。应用异常经过系统异常转换而来,例如新建用户时,发生“主键冲突异常”,则须要在UserinfoDao中将“主键冲突异常”捕获,并转换为应用异常,异常提示信息设为“该用户名XXX已存在,请使用其它用户名”,并将该异常信息反馈给前台。只要系统异常影响到的用户的当前操做,就必须给用户提示信息,好比“系统后台发生错误,请稍后再试”等。应用异常应按照提示方式的异常进行分类,对应不一样的提示页面。 数据库
系统运行日志:记录系统的运行状况,跟踪代码运行时轨迹; 编程
异常和错误日志:记录异常堆栈信息,以供开发人员查看分析; 架构
业务日志:记录业务信息和用户操做,例如用户登陆、删除数据、更新数据等。 app
1、避免过大的try块,不要把不会出现异常的代码放到try块里面,尽可能保持一个try块对应一个或多个异常。 框架
2、细化异常的类型,不要无论什么类型的异常都写成Excetpion。catch语句表示咱们预期会出现某种异常,并且但愿可以处理该异常。异常类的做用就是告诉Java编译器咱们想要处理的是哪种异常,而后针对具体的异常类进行不一样的处理。例如在DAO层中咱们应该只捕获SQLException或DataAccessException(Spring自定义的数据访问异常类)这些数据库异常类,其余的异常NullPointException、NumberFormatException等应经过检测代码增长其健壮性来解决,而不该该经过try..catch(Exception e)…语句捕获全部的异常。 ide
3、不要把本身能处理的异常抛给别人,不要忽略捕获的异常,捕获到后要么处理,要么转译,要么从新抛出新类型的异常。。处理方式包括: spa
Ø 处理异常。针对该异常采起一些行动,例如修正问题、提醒某我的或进行其余一些处理,要根据具体的情形肯定应该采起的动做。再次说明,调用printStackTrace算不上已经“处理好了异常”。 指针
Ø 从新抛出异常。处理异常的代码在分析异常以后,认为本身不能处理它,从新抛出异常也不失为一种选择。
Ø 把该异常转换成另外一种异常。大多数状况下,这是指把一个低级的异常转换成应用级的异常(其含义更容易被用户了解的异常)。
4、若是对catch块尽可能保持一个块捕获一类异常,在catch语句中尽量指定具体的异常类型,必要时使用多个catch。
例:
try {
} catch (Exception e) {
e.printStackTrace();
log.error("UserinfoDao execute() failed");
}
这段代码捕获了异常,但实际上对异常并无进行处理,能够算得上Java编程中的杀手。按照这个方式,在DAO层发生异常被忽略,Service层就认为DAO层运行正确,就不会回滚,事务控制就没有任何做用!!!
Ø printStackTrace对调试程序有帮助,但程序调试阶段结束以后,printStackTrace就不该再在异常处理模块中担负主要责任。
Ø 日志尽可能在系统中的各个出口,例如Action、调度方法等处统一记录,可减小工做量,何况日志"UserinfoDao execute() failed”并无说明异常的详细信息,没有必要向日志输出这句话。
Ø 即便catch中加入throw new RuntimeException(e);语句。若是捕获的异常是RuntimeException,更没有必要将异常再次转化为RuntimeException,这样不只异常的自己的类型丢失,从新定义异常也形成必定消耗。
因此该处的处理原则应是:若是该处异常须要能转化为业务异常反馈给用户,则须要捕捉低级异常并转换成业务异常上抛,不然不作任何处理!!!
6、不要用try...catch参与控制程序流程,异常控制的根本目的是处理程序的非正常状况。
本系统使用SSH框架,DAO+Service+Action三层架构,捕获原则是只有将低级系统异常转化为应用异常的须要才进行捕捉。
各层的处理方式以下:
DAO层:引起DAO异常的问题每每是不可恢复的,如数据链接失败,SQL语句存在语法错误,强制捕捉的检查型异常除了限制开发人员的自由度之外,并无提供什么有意义的做用。所以,Spring的异常体系都是创建在运行期异常的基础上,这些异常都继承于DataAccessException(RuntimeException异常子类),因此,除了出于将低级系统异常转化为应用异常的须要,没有必要捕获异常,让DAO类自动上抛异常便可。
Service层:只捕获自定义应用异常,其余异常上抛。
Action:只捕获自定义应用异常,其余异常上抛。Struts2提供了异常拦截器,拦截器会将定义的异常捕获,记录日志,而后根据配置的异常的类型顺序跳转到相应的页面。配置以下:
struts.xml配置文件
<interceptor-ref name="defaultStack">
<param name="exception.logEnabled">true</param><!--开启日志记录 -->
<param name="exception.logLevel">error</param><!-- 日志级别为ERROR -->
</interceptor-ref>
<global-exception-mappings><!—异常类和跳转页面配置 -->
<exception-mapping result="basicerror" exception="com.***.***.exception.BasicException"/>
<exception-mapping result="error" exception="java.lang.Exception"/>
</global-exception-mappings>
com.***.***.exception.BasicException为自定义应用异常,若是客户端的请求执行过过程当中产生com.***.***.exception.BasicException异常,则会自动转到basicerror页面,从而给用户相应的提示。
basicerror页面
<%@ page language="java" contentType="text/xml; charset=UTF-8" pageEncoding="UTF-8"%>${exception.message }
异常名称 |
说明 |
com.***.***.exception.BasicException |
基础异常类,本系统中全部的自定义异常类均需继承本类 |
com.***.***.exception. DuplicateKeyException |
主键冲突异常,继承BasicException |
例:
Userinfo save方法异常处理
@Override
public void save(Userinfo entity) {
try {
super.save(entity);
} catch (DataIntegrityViolationException e) {
if(e.getCause().getCause() instanceof SQLException){
SQLException sqlE = (SQLException)e.getCause().getCause();
if(sqlE.getErrorCode()==1){//ORACLE主键冲突异常代码
throw new DuplicateKeyException("用户名:"+entity.getUserid()+"已存在,请使用其余用户名");
}
}
}
}
系统中目前配置了三个日志记录器,一个为异常记录器,专门记录异常信息,日志文件到达必定大小后将产生新的日志文件,文件名称为exception.log,另外一个为系统运行记录器,按照日期记录全部的日志信息。