前言
Java作为一门优秀的开发语言,灵活方便的异常处理机制是它的一大特色。以Java语言为基础的SpringMVC框架也集成封装了异常处理机制,提高了软件的可控性和稳定性,可以处理很多软件运行时发生未知的错误,提高用户体验。本文简要介绍一下Spring MVC中的异常处理机制。
问题引入
MVC模型中Controller控制层的作用是接收客户端的请求,调用相应的数据模型进行数据处理,之后传递数据给视图层(客户端)用于前台显示。控制层程序在进行数据处理时候可能会发生某些异常,以下面一个简单的HelloController业务控制器为例。
在代码中添加会引发ArithmeticException异常代码,当出现异常的运算条件时,如程序中除数为0的错误,抛出此异常。
package com.hnust.springmvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/login.form")
public String excute(){
//调用相应的数据模型进行数据处理
int a=4;
int b=0;
System.out.println(a/b);//发生运算异常错误
return "ok";//返回视图组件
}
}
这个错误在编译阶段不会提示错误,只会在运行时发生错误,如果这个错误不使用异常处理机制加以处理,那么运行时会呈现以下界面,对用户很不友好。
此时,可以采用一种简单原始的Try-catch方法处理该错误。
package com.hnust.springmvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/login.form")
public String excute(){
try{
//调用相应的数据模型进行数据处理
int a=4;
int b=0;
System.out.println(a/b);//发生运算异常
return "ok";//返回视图组件名
}catch(Exception e){
//如果发生错误,那么进入error界面
return "error";
}
}
}
当程序发生错误时,重定向到提示错误信息的error界面,这样就增强了程序的健壮性,不至于奔溃。
当然,在SpringMVC中,由DispatchServlet接收请求,由HandMapping组件将请求分发给具体的Controller业务控制器进行处理,所以这类业务控制器是非常多的,要在业务控制器中一一追加Try-catch异常处理模块,也非常麻烦。这时候,使用Spring MVC框架中封装的异常处理机制,就方便很多了。
介绍三种框架级别的异常处理方法。
- 内置异常处理器
SimpleMappingExceptionResolver - 自定义异常处理器
HandleExceptionResolver - 局部异常处理器
@ExceptionHandler
前两种是全局性的异常处理机制,后面一种是局部性的异常处理机制,具体使用介绍如下。
SimpleMappingExceptionResolver全局异常处理
不用在业务控制器可能出错的程序里追加Try-catch模块,只需要在applicationContext.xml中添加SimpleMappingExceptionResolver类Bean定义也可以达到和在程序中使用Try-catch处理异常相同的异常处理效果。
xml中Bean定义为:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error</prop>
</props>
</property>
</bean>
只需要进行如上配置,当业务控制器的代码出现运行错误时,抛出的错误信息就会被该对象捕获,然后返回的提示出错的视图名(error),视图名会交给ViewResolver的配置寻找到具体的视图组件(error.jsp),返回前台界面提示错误信息。
这种内置的异常处理器是全局性的,也就是说整个程序中所有的业务控制器在调用其他组件处理数据发生异常时,如果抛出异常信息,就会做出相同的处理。
当控制器执行登录跳转操作时发生运算异常时:
HandleExceptionResolver全局异常处理
HandleExceptionResolver是一个用于自定义异常处理器的接口,所以需要自己编写一个异常处理类来实现该接口,从而达到异常处理的效果。
这种自定义异常处理可以对不同类型的异常进行不同的处理,还可以提供增加日志记录的功能。
使用步骤
- 编写一个自定义处理异常的类,实现HandleExceptionResolver接口
- 在applicationContext.xml中添加该实现类的Bean定义
<bean class="com.hnust.springmvc.MyExceptionResolver">
</bean>
- 如果自定义针对不同的异常类型进行不同的处理,自定义异常类代码可以如下编写
package com.hnust.springmvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class MyExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// TODO Auto-generated method stub
ModelAndView mav = new ModelAndView();
if(ex instanceof ArithmeticException){
//处理运算异常
mav.setViewName("ae_error");
}else if(ex instanceof NullPointerException){
//处理空指针异常
mav.setViewName("npe_error");
}else{
//处理其他异常
mav.setViewName("error");
}
return mav;
}
}
- 如果需要在异常处理时追加异常日志记录的功能,自定义异常类代码可以如下编写
package com.hnust.springmvc;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.objenesis.instantiator.basic.NewInstanceInstantiator;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class MyExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// TODO Auto-generated method stub
//追加日志记录
try {
FileWriter fw;
fw = new FileWriter("D:\\error.log",true);
PrintWriter pw =new PrintWriter(fw);
ex.printStackTrace(pw);
pw.flush();
pw.close();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ModelAndView mav = new ModelAndView();
mav.setViewName("error");
return mav;
}
}
这样可以处理异常,也可以记录下异常错误信息。
@ExceptionHandler局部异常处理
前两种都是全局性的异常处理,而使用 @ExceptionHandler注解可以处理个别Controller的异常处理。
新加一个DemoController业务控制器
package com.hnust.springmvc;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class DemoController {
@RequestMapping("/login2.form")
public String excute(){
//调用相应的数据模型进行数据处理
int a=4;
int b=0;
System.out.println(a/b);//发生运算异常
return "ok";//返回视图组件名
}
//局部异常处理
//个性化的异常处理
@ExceptionHandler
//方法名可以自己定义,参数类型要按要求写,否则运行会发生错误
public String handlerExcepting(HttpServletRequest request,Exception ex){
return "he_error";
}
}
在代码中加入局部异常处理,这样程序发生异常时会优先被局部异常处理器接收处理,不会再被全部异常处理器处理,并且该局部异常处理器只处理DemoController控制器发生的异常,不会处理其他的业务控制器发生的异常。