转自:http://www.cdtarena.com/javapx/201305/8602.html
不管是什么程序开发都可能会出现各种各样的异常。可能是程序错误,也可能是业务逻辑错误。针对这个各个开发人员都有自己的处理方式,不同的风格增加了业务系统的复杂度和维护难度。所以定义好一个统一的异常处理框架还是需要的。我们开发框架采用java实现,java中的异常一般分为两种,检查异常和运行时异常。检查异常(checked exception)有可能是程序的业务异常,这种异常一般都是开发人员自定义的、知道什么时候会抛出什么异常并进行捕捉处理。也可以是系统的异常,不捕捉编译不会通过,如 IOException、SQLException、ClassNotFoundException , 这种是必须要捕捉的并且大多都是继承Exception。 运行时异常一般都是系统抛出来的异常,这种异常不捕捉处理也不会报编译错误,如NullPointerException,ClassCastException。运行异常都是继承至RuntimeException。不管是检查异常还是运行时异常都是继承至Exception。另外还有一种异常是系统错误Error,这种异常是系统出现了故障抛出来的不能捕捉,如OutOfMemoryError。Exception和Error都是继承至Throwable。
了解了java的异常体系后,我们设计一下web框架的异常处理格式。在以往EJB时代的J2ee系统,一般是标准的三层架构:web层、业务逻辑层、数据访问层,并且每一层都分别部署在不同的机器集群中。这样我们的异常一般分为三个,WebException、BizException、DAOException分别映射到web层、业务逻辑层、数据访问层。并且这些异常都要设计的串行化可以跨机器传递生成异常链。这样的好处是看到异常链知道从哪儿抛出来的错误,比较清晰明了。
随着后面spring的推出,java的开发越来越轻量级很多时候一台服务器可以同时部署三层并集群化,架构模式也慢慢由充血模式演变为贫血模式,再也没有了厚重的实体Bean和有状态会话Bean。针对现在轻量级的框架,异常结构如何设计呢?
先看看我们这个异常结构需要解决的问题是什么?
1、规范大家的异常处理方式。
2、简化异常处理。
3、区分业务异常和系统异常,业务异常需要业务逻辑支持,系统异常需要记录log。
4、友好的异常展示。
5、异常结构可扩展。
针对这些点,我的想法是开发可以使用三个类:SDKException、BizException、BizSystemException。SDKException是处理的基础类,可以在里面封装一些异常处理的基本函数。BizException是业务逻辑处理异常,一般这类异常是不需要记录log,只是展示给页面显示并提示给用户。如你的用户名、密码为空等错误。BizSystemException是业务系统异常,这类异常一般需要捕捉并记录log,比如数据库的主键冲突、sql语句错误等。按照三层架构的话,我们不可能对每一层都捕捉并且记录log,会造成重复log。可以从DAO层把捕捉到的数据库异常转换为BizSystemException抛出,如果有BizException也抛出。业务逻辑层对于BizSystemException、BizException不处理直接抛出。所有处理都在web层进行集中处理,如果是BizException,根据错误码和错误消息显示给用户对应的页面和错误消息。如果是BizSystemException告知用户系统错误,并把错误结果记入log。如果是其他异常和BizSystemException一致。这样就减少了异常处理的复杂度,开发也不用关心什么检查异常,运行时异常。
如果是ajax请求在做web层时,把返回的jsp变成json格式或者流格式输出即可,不影响异常框架。如采用struts2结构的代码:
public String testAjax()
{
try
{
genAjaxDataStr(0, "{}");
} catch (BizException e)
{
getRequest().setAttribute(this.ERRORMESSAGE, e.getErrorMessage());
return this.ERRORJSON;
} catch (BizSystemException e)
{
getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR);
return this.ERRORJSON;
} catch (Exception e)
{
this.errorTrace("test", e.getMessage(), e);
getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR);
return this.ERRORJSON;
}
return this.NONE;
}
这样前台就能根据我们的异常显示对应的错误页面了,并能把系统知道的和未知的异常记入log。
针对struts2还有个问题,在开发模式时,struts2和webwork的异常打印在页面,我们可以根据页面输出进行调试。一但部署在生产环境,需要将这个模式关闭,log就没有了。
<constant name="struts.devMode" value="false" />
为了记录一些未知的错误,需要做以下步骤:
1、将全局的异常映射页面从struts2的包定义里去掉。如果不去掉,在webwork不会抛出异常也就找不到出了什么问题。
2、扩展struts的DispatcherFilter捕捉未知的异常并记录入log。
<global-exception-mappings>
<exception-mapping exception="com.linktong.sdk.biz.exception.BizException"
result="checkedException" />
</global-exception-mappings>
这样,基本的异常框架就搭建完成。更进一步需要做的是:
1、分布式全局错误码体系,保证所有机器都共用一套错误码。
2、分布式部署,异常传递。可以采用hessian序列化错误码的机制,不用传输整个异常链节省带宽。
3、集中logger服务处理,所有机器的log统一发送到集中服务器处理。logger框架和log4j也有服务器的机制。
针对web框架、分布式部署、log服务器再讨论:)