`
寻觅一只耳朵
  • 浏览: 34175 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

j2ee异常拦截

    博客分类:
  • java
阅读更多

原文出自:IT博客吧(http://www.itblog8.cn/java/20130530133.html

在实际的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;
}

 

分享到:
评论

相关推荐

    J2EE应用开发详解

    内容为J2EE应用开发详解中的源代码 第1章 Java Web应用开发简介 1 1.1 Java EE应用概述 1 1.2 Java EE概念 1 1.2.1 Java EE多层模型 1 1.2.2 Java EE体系结构 2 1.3 Java EE的核心API与组件 4 1.4 Web服务器和应用...

    从J2SE到J2EE知识点介绍

    1. struts2的拦截器定义以及使用 153 2. struts标签 160 3. Struts 2 标签库说明及使用 160 4. set 描述 169 5. text 描述 170 6. property 描述 170 7. Struts的异常处理 171 8. Struts的上传与下载 178 五、 ...

    Aspect自定义springboot的使用.docx

    切面(Aspect):是指横切多个对象的关注点的一个模块化,事务管理就是J2EE应用中横切关注点的很好示例。在Spring AOP中,切面通过常规类(基本模式方法)或者通过使用了注解@Aspect的常规类来实现。 连接点(Joint ...

    【JeeSpringCloud v3.2.4】后台权限管理系统+互联网云快速开发框架+微服务分布式代码生成.zip

    异常邮件监控:全局拦截系统异常,并发送通知邮件。 单点登录:使用shior和Redis、共享session方式实现单点登录。 Redis分布式高速缓存:代码生成直接生成Redis对接代码。 特性: JeeSpringCloud基于SpringBoot+...

    【JeeSpringCloud v3.2.4】后台权限管理系统+互联网云快速开发框架+微服务分布式代码生成

    异常邮件监控:全局拦截系统异常,并发送通知邮件。 单点登录:使用shior和Redis、共享session方式实现单点登录。 Redis分布式高速缓存:代码生成直接生成Redis对接代码。 特性: JeeSpringCloud基于SpringBoot+...

    java面试题

    答:Struts1和Struts2是两个完全不同的框架,Struts1以ActionServlet作为核心控制器,由ActionServlet负责拦截用户的所有请求。Struts2以核心控制器FilterDispatcher为基础,包含了框架内部的控制流程和处理机制。 ...

    spring in action英文版

     1.1.1 J2EE开发者的一天  1.1.2 Spring的承诺  1.2 Spring是什么  1.3 开始Spring之旅  1.4 理解反向控制  1.4.1 依赖注入  1.4.2 IoC应用  1.4.3 企业级应用中的IoC  1.5 应用AOP  ...

    JeeSpringCloud后台权限管理系统-其他

    异常邮件监控:全局拦截系统异常,并发送通知邮件。单点登录:使用shior和Redis、共享session方式实现单点登录。Redis分布式高速缓存:代码生成直接生成Redis对接代码。特性:JeeSpringCloud基于SpringBoot+SpringMVC+...

    Spring面试题

    4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

     9.2 利用拦截器(Interceptor)生成审计日志  9.3 Hibernate的事件处理机制  9.4 批量处理数据  9.4.1 通过Session来进行批量操作  9.4.2 通过StatelessSession来进行批量操作  9.4.3 通过HQL来进行批量操作 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     9.2 利用拦截器(Interceptor)生成审计日志  9.3 Hibernate的事件处理机制  9.4 批量处理数据  9.4.1 通过Session来进行批量操作  9.4.2 通过StatelessSession来进行批量操作  9.4.3 通过HQL来进行批量操作 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

     9.2 利用拦截器(Interceptor)生成审计日志  9.3 Hibernate的事件处理机制  9.4 批量处理数据  9.4.1 通过Session来进行批量操作  9.4.2 通过StatelessSession来进行批量操作  9.4.3 通过HQL来进行批量操作 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

     9.2 利用拦截器(Interceptor)生成审计日志  9.3 Hibernate的事件处理机制  9.4 批量处理数据  9.4.1 通过Session来进行批量操作  9.4.2 通过StatelessSession来进行批量操作  9.4.3 通过HQL来进行批量操作 ...

    hibernate 框架详解

    J2EE应用程序服务器的集成 4.8.1. 事务策略配置 4.8.2. JNDI绑定的SessionFactory 4.8.3. JTA和Session的自动绑定 4.8.4. JMX部署 5. 持久化类(Persistent Classes) 5.1. 一个简单的POJO例子 5.1.1. 为...

    最全Hibernate 参考文档

    3.8. J2EE应用程序服务器的集成 3.8.1. 事务策略配置 3.8.2. JNDI绑定的SessionFactory 3.8.3. JTA和Session的自动绑定 3.8.4. JMX部署 4. 持久化类(Persistent Classes) 4.1. 一个简单的POJO例子 4.1.1. 为...

    Hibernate3+中文参考文档

    3.8. J2EE应用程序服务器的集成 3.8.1. 事务策略配置 3.8.2. JNDI绑定的SessionFactory 3.8.3. JTA和Session的自动绑定 3.8.4. JMX部署 4. 持久化类(Persistent Classes) 4.1. 一个简单的POJO例子 4.1.1. 为...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    8.8 拦截器 332 8.9 依赖注入 335 8.9.1 EJB注入 336 8.9.2 资源注入 339 8.10 配置EJB引用 340 8.11 使用计时器进行任务调度 342 8.12 本章小结 345 第9章 消息驱动EJB 346 9.1 JMS和EJB 347 9.1.1 为什么使用MDB ...

    Spring中文帮助文档

    3.10. 以J2EE RAR文件的形式部署Spring ApplicationContext 3.11. 基于注解(Annotation-based)的配置 3.11.1. @Autowired 3.11.2. 基于注解的自动连接微调 3.11.3. CustomAutowireConfigurer 3.11.4. @...

Global site tag (gtag.js) - Google Analytics