异常管理(java)

前言

程序运行时出现异常是很常见的,有时候在本地测试都没问题,一旦部署到生产服务器运行一段时间就出现问题。这个时候,我们就应该在代码中对可能产生异常的地方(常见的对数据库的操作)编写异常处理的代码,或是响应给客户端,或是将异常记录到日志(比如log4j),然后在进行处理。本文主要讲的是个人对 try catch 和throws对异常的处理认识。

对try catch的认识

这个常用的是try{code}catch(){}来对try里面的代码进行检查,如果运行时出现异常,就会进入进入catch 里面的代码,那么有个小问题,当catch 完之后,程序是否有返回值(如果这个方法有返回值),项目是否会继续运行??

我们先来看下面的测试代码:

public class TestTryCatch {

    public static int myTest(){
        int i = 0;
        try {
            int x = 5;
            int y = 0;
            int z = x/y;
        } catch (Exception e) {
         i= 1;
        e.printStackTrace();
        }
        return i;
    }

    public static void main(String[] args) {
        int i = myTest();
        System.out.println("i="+i);
    }

}

对于这个简单的例子,在不放进编辑器运行的时候,你觉得i的值会是什么??下面有三种选择:A、0 B、1 C、程序停止,没有返回值。
最终运行结果是:B 1 。

这个简单的例子,其实跟我之前的认识是有区别的。我一直认为当程序遇到异常,也就是try catch 之后,程序不会有返回值,程序在catch 之后会停止,但是很明显我的认识是错误的。其实这里在编码的时候,如果是我们以我的认识,那么将会导致一个bug,请看下面的例子:比如说有个送流量的活动,每次输入号码的时候要先查询是否领取,如果领取了将不能再领取。

public static  String checkUserTraffic(String number,String returnCode,String nowTime) {

            String flow = null;
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                String sql = "select  flow  from FLOW_INFO where PHONE = ? and RETURNCODE = ? and RECIEVETIME = ?";
                conn = GreenetHomeConnecFactory.getConnection();
                ps = conn.prepareStatement(sql);
                ps.setString(1, number);
                ps.setString(2, returnCode);
                ps.setString(3, nowTime);
                rs = ps.executeQuery();
                while (rs.next()) {
                    flow = rs.getString("FLOW");
                    System.out.println("flow:" + flow);
                }
            } catch (SQLException e) {
                Log.error("查询是否领取流量失败,参数"+"number="+number+",returnCode="+returnCode+",nowTime="+nowTime+"异常信息:"+e.toString());
            } finally {
                // conn.setAutoCommit(true);
                GreenetHomeConnecFactory.close(conn, ps, rs);
            }

            return flow;
        }

大家有没有看出代码中隐藏的bug?没错,就是没对异常做出正确的处理,这里对异常处理只打印了输入参数。但是你发现没有,如果程序遇到异常,或者数据库不存在那个用户的记录,flow都会返回null;比如说当用户输入已经领取过流量的号码时,理论上是不能在领取的,但是这时候程序出现异常,返回的是null,那么他将会成功的再领一次流量,天大的漏洞啊。所以在这里有个比较的做法是,在catch这里给flow赋值,比如说给个error,然后控制器可以知道遇到了异常,直接将结果返回,不需要再继续下去,这样可以避免损失。

404找不到页面处理

这个其实比较简单,做个用于展示404的页面,然后在web.xml配置即可。

   <!--  设置错误页面 -->
<error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/views/error/404.jsp</location>
    </error-page>

全局异常处理器

其实这个才是我想说的重点。关于全局异常处理器,很多博客都会有介绍,我要说的是完善全局异常的输出信息。

首先全局异常器是在springmvc中配置的

   <bean id="resolveExceptionclass="com.aotain.read.controller.ExceptionHandlerController"/>

然后当遇到异常的时候,正常来说,如果没有try catch处理,你可以写throws 或者不写,他都会进入异常处理器。下面是处理器的代码:

public class ExceptionHandlerController implements HandlerExceptionResolver{

    private static Logger Log = Logger.getLogger("sysLog");

    public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {
        //转发到异常页面并输出打印日志(对于异步请求不会转发页面)
        ModelAndView mav = new ModelAndView("error/500");
        Log.error("异常信息:"+ex);
        return mav;
    }
}

不知道大家有没有发现个问题,这里对异常的处理不是很好,返回一个视图告知用户,还行;但是没把具体出异常的堆栈信息(具体是什么异常,在那些代码中出现这个异常)打印出来,这对于我们发现和解决问题造成一定的困难,因为出现异常的时候,单凭简单的异常类型就去主观臆断,不是很好。
所以我就去百度了下Exception 都有哪些方法。
一开始我发现的就是这三个:

返回类型        方法                    结果
String         getMessage()     异常的类型
String        ex.toString()         异常的类型
               printStackTrace()    完整的堆栈信息

第三个方法的结果就是我们需要的,但是返回类型为void,但是其它两个方法又不够具体。后来我看到了这个博客Java 打印堆栈的几种方法,我从这里找到了答案,修改了写法

/**
 * 全局异常捕捉器
 * @author huangjg
 *2017年10月27日
 *
 */
public class ExceptionHandlerController implements HandlerExceptionResolver{

    private static Logger Log = Logger.getLogger("sysLog");
    public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {

        ModelAndView mav = new ModelAndView("error/500");//转发到异常页面并输出打印日志(对于异步请求不会转发页面)
        StackTraceElement[] sTraceElements = ex.getStackTrace();
        Log.error("异常信息:"+ex.getMessage());
        for (int i = 0; i < sTraceElements.length; i++) {
            Log.error("异常位置:"+sTraceElements[i].getClassName()+"."+sTraceElements[i].getFileName()+"."+sTraceElements[i].getMethodName()+":"+sTraceElements[i].getLineNumber());
            }

        return mav;
    }
}

通过遍历sTraceElements和log4j的结合,一方面可以记录出现异常的堆栈信息,一方面也可以也可以给客户端一个响应,并且我做测试的时候,不会像try catch一样会有返回值(如果有返回值),而是直接进入了异常机制,这样程序不会照着正常的流程走下去。不过这里要注意的是,如果异常被你try catch到的,就会进入catch 处理,就算你配置了全局异常处理器,也不会进入异常处理器那里。但是我也发现个小问题,如果是客户端发情ajax异步请求,也就是说出现异常的时候不会发生跳转,这样就无法通知客户端唉,这里还没找到合理的解决方法。

结语

以上是我对异常管理简单的认识,如果有朋友发现我写的不好或者有什么好的意见的都可以给我留言,大家交流交流。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值