事情起因】 最近在看《Java编程思想》第4版,昨天看到异常处理那章时,就想到了J2EE项目中异常的处理,J2EE的项目光在后台打印出异常还是不够的,需要以一种友好的方式提示给用户。
     【事情经过】  于是我就看了之前在公司做的项目,这才发现,在struts里,我们都是
               try{
                    xxxxxxxxxx
                }catch(BunisessException e){
                    throw new BunisessException("xxXXXXX,请确认后重试");
                }
             就是自己将我们自定义的异常抛出去,却重来没有管他如何在页面上展示。我运行起项目,当故意进行错误操          作时,后台我throw Exception的信息,总能以很友好的对话框显示出来。仔细查看没个页面,却没有任何的异          常的提示信息。【思考】那么异常信息时如何弹出来的?
        我直觉告诉我,肯定有一个公共的页面用来进行错误提示。带着这个问题,我看了项目中所有的公共页面的代码。
        直到发现一个ErrorAler.jsp的页面。这个页面比较简单,在页面上写了一段JAVA代码,调用了后的一个类的静态
        方法,我跟踪进去,查看该类,发现他继承了一个来自acegi的类,并实现了一个借口springFramework.security
        的一个借口。
        
        处理代码很短,但是涉及到其他几个类,大致的意思是:获取到异常堆栈,最后抛出的那个异常,然后getMessage()
        然后放置到session中。
        
         这个页面最后会以window.open()的方式打开,看起来就会像一个alert框。
         他的实现原理和机制,我还是没懂。由于我家没网,所以不能把代码发上来供大家一起研究。高手能不能大致讲讲这种处理机制?我所知道的异常处理好像主要有以下几种办法:
1.web.xml里面有个error-page2.用struts 有个 globel-exception3.在后台catch 语句块 用request派发到页面,在页面上show
  【写在后面】  我会继续去研究,等研究透了,会和大家一起分享的!

解决方案 »

  1.   

    以IBM为例,异常哪些要抛,哪些不抛,哪些要写到日志里面都有比较明确的规定的。按照规定走。把异常当做一种逻辑的一部分看待。
      

  2.   

    我所知道的异常处理好像主要有以下几种办法:
    1.web.xml里面有个error-page2.用struts 有个 globel-exception3.在后台catch 语句块 用request派发到页面,在页面上show楼主想的不错, 这就是一个大体的流程。 也就是说, 我们把本来在控制台打印的异常信息在页面上也打印了。这对于给懂技术的人来看是由意义的, 但是对于那些单纯面向客户的程序来说, 就没有必要再页面上来显示异常了。
      

  3.   

    对于楼主所说的用户操作异常这类的,我的做法是将错误信息放到struts的ActionError中,然后在视图层处理:
           1,新建一个页面,显示“尊敬的用户您好,您的操作出现了以下错误:”,然后列出ActionError里面的错误内容。
           2,在原页面直接显示错误信息。
           3,用Javascript控制弹出错误信息提示框。
    对于那些非用户操作的异常,比较简单的错误就是跳到一个error.jsp,网站类型的就告知"对不起,系统正在维护!",非网站的企业级管理系统就告知“系统运行错误,已记录错误日志,请联系维护人员!”。然后记录错误信息日志。这符合大多数程序员的懒汉思想,包括我,不是不想研究更完善、通用、简易、准确的异常处理方式,觉得够用了就不研究了,经楼主这么一提醒越发觉得我们可耻了,期待有高手整理。
      

  4.   


    好像是这样的,strust只管throw  实现了spring的那个借口以后 会有一个afterThrow()方法,在这个方法里讲Exception加入进去,然后重写acegi那个什么ExceptionHandler类的方法,去处理 。。但是整个过程,思路还是不太清晰。请问如何确保出现异常以后的都跳转到那个错误提示页面??
    struts有<globleException> 吗?  但是如果直接用Servlet勒?
      

  5.   

        之前对于异常处理,百分之八九十都是直接给吞了。说到这个吞字,大家可能不太明白我的意思。但是e.printStackTrace();这条语句如果你在自己的程序中用的比较多的话,那就说明你也是吞异常的高手。然而很遗憾,成为了吞异常的高手,就一定是一个处理异常的低手(这个词太别扭了,改用'菜鸟'吧)!如果把不该吞的异常也给吞了,那就降低了程序和用户(包括二次开发人员、测试人员以及普通用户)之间的交互性。程序如果在出错的时候总是保持沉默,那用户不爽的次数到达一定数量后,最终将极有可能把我们所谓的作品给抛弃。异常处理的重要性已经体现出来了,要不然SUN公司也不会专门搞个异常处理机制了。说到这里,真正需要思考的问题才刚刚开始,如何才能更好地或者说更为合理地进行异常的处理呢?要想成为异常处理的高手,首先肯定得了解产生异常的原因,然后根据原因进行相应处理。究竟是抛,还是不抛(或者说'吞'),这是一个问题;究竟是使用默认异常还是自定义异常,这同样也是一个问题。如果异常是系统内部引起并且是我们能够确定的,比如说一个字符串s在使用getBytes("UTF-8")时出现UnsupportedEncodingException异常,那么就可以把它吞了(或者抛出一个Error),因为UTF-8是系统支持的基本编码集之一;如果异常是我们无法确定的,那此时就应该向上抛出,比如我们在写一个底层函数,需要进行文件的读写操作,那么很显然会有一个FileNotFoundException异常,这时就应该抛出去;但是如果我们站在更上一层,能够直接面向用户了,这时就得进行异常处理,比如来个信息提示说相应的文件没找到。接着说说自定义异常给我们带来的好处。自定义异常的好处个人认为主要有两点:一是能够更好的进行异常定位;二是能够将同种性质或是类似性质的异常进行归类,向上抛出后方便别人处理。比如给别人提供了一个二次开发函数,作用是读取一个Xml格式的文件,并且通过一系列解密操作,得到原始信息,然后通过这些信息进行相关类的构造。既然是读取文件,那么必然会有一个FileNotFoundException异常,又由于是一个经过加密的xml文件,那么在读取操作时也很有可能会产生诸如xml文件格式不正确、xml节点错误、xml节点内容错误等各种异常情况。对于二次开发人员来说FileNotFoundException异常很方便处理,但后面的几种异常可能会使他产生厌烦。此时,我们就应该使用自定义异常将xml文件格式不正确、xml节点错误、xml节点内容错误等各种异常归并起来,然后向上抛出。于是对于二次开发人员来说,他只需要处理两个异常,一个是FileNotFoundException,另一个则是我们自定义的异常,比如FileContentInvalidException(文件内容无效)异常。因为产生文件内容无效的原因,多半是由于正确产生出的xml文件被非法修改过所造成的。当然了,自定义异常的名称最好也能够见名知意!
      

  6.   

    lz你把struts和spring的源码全部看一遍就知道了
      

  7.   

    如20楼所说 e.printStackTrace(); 不可用。
      

  8.   

    那是肯定的。比如我问自己定义了一个BException   专门来处理用于错误操作或是不符合业务流程的操作引起的异常,
    这个我们肯定要抛出来给用户告诉他:你的XXX操作错了。
     
    如果在ACTION层只管抛出来就可以。。怎么样实现这个异常处理的框架? 让程序发生异常的时候,都会跳转到那个页面,打印出提示信息?
      

  9.   

    我们现在的项目是用
    struts2  globel-exception
      

  10.   

    我之前的项目是struts1.2  看 代码号像是用了spring和acegi里面的东西
      

  11.   

    opennms 这个开源项目为例  它就是为一个filter来处理的。
      

  12.   

    发生异常,跳转页面,可以在web.xml里配置 
      

  13.   

    我觉得异常处理要站在调用者的角度考虑.
    还有就是一般出现异常了就应该一层一层地往上抛,除非是可以容忍的异常.
    比如NumberFormatException之类,系统内部也要做相应的容错处理
      

  14.   

    我以前做过Oracle的ADF框架,里面就是有公用出错页面的呢
      

  15.   

    我一般是这么解决的 在web.xml里面配置的 例如
    <!-- 配置错误信息 -->
    <error-page>
    <error-code>404</error-code>
    <location>/FileNotFound.jsp</location>
    </error-page>
    <error-page>
    <error-code>500</error-code>
    <location>/error.jsp</location>
    </error-page>
    <error-page> 
            <exception-type>java.lang.NullException</exception-type> 
            <location>/error.jsp</location> 
       </error-page>
    <error-page>
    <exception-type>java.lang.NoSuchMethodException</exception-type>
    <location>/error.jsp</location>
    </error-page>
    <error-page>
    <exception-type>java.io.IOException</exception-type>
    <location>/error.jsp</location>
    </error-page>
      

  16.   


    web.xml 还可以配置exception-type  学习了,
    回家了再研究一下,那个异常框。 等研究透了来跟大家分享
      

  17.   

    我们的异常是首先定义异常code的枚举类,如:public int PMS_ERROR=10001
    与properties文件如:10001=PMS ERROR!
    然后把ERROR COde往外面抛,最后response异常code, 最后在JS页面获取的异常信息http://topic.csdn.net/u/20100316/18/e5a61c83-4801-469d-a365-d8862ee5c48f.html?seed=1478034688&r=63964429#r_63964429这样做有点不好,就是必须把所有的properties都弄到页面.  
     你也可以直接返回异常提示!
      

  18.   

    其实我觉得我们项目的异常提示是做的非常友好的,  但是JS太多了,看起来太麻烦了. java代码20W, JS代码15W..  简直就是BT. 所有页面没有input, button等等标签. 
      

  19.   

    纠正下我们的异常是首先定义异常code的枚举类,如:public int PMS_ERROR=10001
    改为:
        /** 网络异常 */
        ERROR_NETWORK(0X2001),    /** RMS通信异常 */
        ERROR_RMS_COMMUNICATION(0X2002),    /** 数据库异常 */
        ERROR_DB_OPERATION(0X2003),    /** 名称重复 */
        ERROR_NAME_DUPLICATE(0X2004),
      

  20.   


    !!!  其实我是想发贴问一下,有没有人懂我说的那种抛异常的方式。。 里面涉及到AOP的我那个项目,光js都有12M。
      

  21.   

    发生异常,跳转页面,可以在web.xml里配置 
      

  22.   

             我不知道LZ怎么想的。我只知道对于可能出现的一些异常,显示的把它输出出来就行了。着时候输出的方式就多了。弹不弹得就无所谓了。只要不让程序崩溃就行了。至于你输出的方式哪看你自己的爱好了。
          感觉LZ钻牛角尖了。想多 了。
      

  23.   

    我以前最讨厌看到那些异常信息了,后来有一次不小心没有让异常打印,我费了一上午的时间寻找错误,结果竟然是一个SQL语句错误,而且捕获异常后并没有打印,我差点晕过去。
      

  24.   

    果子哥,问题就是如何把他们关联起来。简单的说要实现如下功能:     action层只管抛异常就好了,页面上也不用管异常信息。
         当发生异常时候,会有一个方法去获取到异常的信息,配合web.xml弹出一个页面,吧提示信息打印出来。
      

  25.   

    首先我觉得肯定设计AOP 拦截
      

  26.   

    所以我看到他的处理类,集成了一个acegi项目里的类,并且实现了spring.sercurity包的一个接口
      

  27.   


    没看懂!action 层只管抛异常?action 应该是相当于 Servlet 的东西吧,这已经是服务端最后的防线了,对于检查型异常都必须处理掉,而不是抛出或者吃掉。
      

  28.   

    如果想要细说异常的用法,就算排除JVM的机理部分,也可以总结出很厚的一本书。单就WEB系统开发而言,说一个技术员在系统开发方面,是否入门了,那么,这个人对异常处理的理解如何,是一个很重要的评测点。
      

  29.   

    String strHtml = "<html><head><script type='text/javascript'>window.onload=function() { window.parent.alertError();}</script></head><body></body></html>";
    doAlert(response, strHtml);
    public static void doAlert(HttpServletResponse response, String html) throws IOException {
    OutputStream os = response.getOutputStream();
    OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
    response.setContentType("text/html;charset=UTF-8");
    osw.write(html);
    osw.flush();
    osw.close();
    }jsp页面上个js  
     function alertError(){alert('')}哪里有错调用doAlert  js写个方法输出文字
      

  30.   


    是的,action是最后防线。
    我们现在假设,存在以下情况:
    假设正常情况下用户的操作流程如下:先操作A---》再操作B----》再操作C
    假若用户现在他这样操作:先操作A----》再操作C------》再操作B以上列,只是我假设的一情况,我们设计和JS验证的问题不谈。就是说当用户操作失误,或者说用户的不当引起错误。这个错误一般都是我们在自定义的异常。我们这里假定他为BusinessException.在项目中,其它的异常我们都应该处理掉。但是当发生BusinessException时,我们就应该中断程序,并在页面上提示用户(操作失误,或者其它云云)。正如你所言,action层是最后防线。此时我们在action用try...catch来处理所有的异常。
    当我们catch到BusinessException的时间,在我的项目中,我们的做法都是throw出去。代码如下:  catch(BusinessException e){
         throw new BusinessException("必须先进行B操作,再进行C操作");
      }然后在客户端这边就会弹出一个页面,把“必须先进行B操作,再进行C操作”显示出来。我见过有些公司是这样写样。  catch(BusinessException e){
          request.setAttribute("errorMsg","必须先进行B操作,再进行C操作")
      }然后在每个页面会都用一个DIV来显示错误信息。但是我之前的那个公司,并没有这样做,有一套自己异常抛出框架。也就是说专门有一个异常处理类。来处理我们抛出到页面的异常。这个类继承了一个来自acegi包的一个类,这个类名我记不住了,名子很长,结尾是Handler,该类还实现了一个org.springFramework.sercurity包一个接口,名子也记不住了。这个框架的效果就是这样的:
    假设你在后台,如果程序出现异常(通常都是业务流程操作失误),需要给客户端的用户提示,你就throw一个Exception。但是在你的页面上,你根本什么都不需要去考虑,不用去关心任何与异常有关的东西,若后台有throw Exception,前端会弹出一个页面把提示信息打印出来。这样就完全没有耦合
    我就想问一下,有没有人用过这样的异常抛出?有人懂原理不?
    等改天我把家里的代码发上来,大家一起研究一下。是我们之前的一个架构师写的。
      

  31.   


    小野人,你这个用户,就跟我说的有些公司用request.setAttribute()大同小异。页面人员还是需要去考虑异常的问题。另外的公司的那种抛异常,并不是大家说的直接跳转到一个页面。。页面还是在当前页面。只是打开一个对话框大小的页面,显示一下提示信息,你关掉他,还可以继续操作。
      

  32.   

    异常的处理,通常会面向两端 前台(客户端) 后台(服务器端)
    前台的处理是把相对友好的信息提示给用户,避免用户直接看到异常信息.目前一般会使用一个或多个公共的异常页面进行回显.(例如暴雪的网站,你可以任意输入不存在的网页URL,暴雪为了避免让用户直接看到404页面从而降低对网站的体验满意度,所以统一会跳转到一个公共的处理404错误的页面 例:输入http://us.blizzard.com/en-us/abc)
    后台的处理是把相关的错误 通过专门的日志模块进行记录,而不是打印输出在控制台. 为技术人员在处理时提供详细的资料(除简单项目会用到log4j等简单的日志模块外,那些大型项目都会拥有一个独立的日志模块).
    -----
    异常的处理原则: 避免因异常导致整个项目瘫痪,避免直接让用户直接看到异常信息,造成体验度下降.在容错并呈显给用户借口页面的同时,将真正的原因在后台进行记录.
      

  33.   

    自定义异常,将已知或未知的错误信息保存到自定义类的属性里,在action封装到request跳转到JSP专门的错误页面。取出来就是一个友好的提示界面了。特殊异常直接定义,类似“数据库未知异常,请联系管理员!”
      

  34.   

    用feilderror打印到页面就行了。
      

  35.   

    这个其实就是根据异常类型而定 有些异常我们只需要知道他曾经发生过便可以了 因为不抛出异常我们一定要在catch块给他返回一个"正确结果" 比如我做过一个通过Servlet读取硬盘上图片的东西 我无法保证目标图片是否存在 我只能保证在图片不存在的时候提供一个 "读取错误的图片" 而异常 只需要 e.printStackTrace();下就够了 至少能让身为程序开发者的你知道发生了什么处理异常的原因是为了避免用户看到程序内部太多的东西(也可以说成是用户看的懂当前发生了什么状况) 所以做法要么是通过函数的返回结果 或者在外面捕捉异常 但是都会把异常信息转换成给客户看的错误 比如"数据库维护中"阿 或者是"目标页面不存在"阿等等 
      

  36.   

    用户操作产生的异常,这个要用心去分析,有时候可以用javascript判断
      

  37.   

    在j2ee项目里异常一般会分为两类,
    一类是你代码里写的BunisessException ,这类异常看名字你就知道了表示的是业务异常,比如输入的数据不合标准,不合业务逻辑等等,这样的异常是可以修正的——在页面上提示给用户,让用户重新输入即可第二类属于系统异常,就是不可修正的异常,这种异常都继承自RuntimeException,在进行数据库操作过程中的像SQLExceptionn,ConectException之类的我们会将其扑获后转换成RumtimeException。因为这些异常是不可能通过用户更改输入就解决的在页面显示上当然也分两类了,对于一般业务异常就在用户操作的页面上提示用户输入不合法,修改输入项。
    对于系统级的异常会用个专门的页面显示异常信息,并记录到日志里。(我们公司的系统异常页面上提供了发送邮件功能,可以将异常信息Email给系统管理员 :)  )
      

  38.   

    我之前做的一个项目用的SSI框架
    是在STRUTS里配置global-exception-mappings属性,所有异常都指定到一个ERROR页面
    然后每个ACTION里,出异常都是用addActionError
    然后ERROR页面上用STRUTS标签s:actionerror来获取
    不知道这个是不是LZ的意思
      

  39.   

    如果是.Net里的话在Global.asax里的Application_Error方法里处理就可以了,好像java里没有对应的方法,找了一下似乎可以定义一个Filter在doFilter(ServletRequest request, ServletResponse response,FilterChain chain)方法里面处理