查看 @ControllerAdvice源码可见,添加了@Component;
则@ControllerAdvice是spring的一个组件,可理解为一个实体Bean,
可被 <context:component-scan base-package="com.hesvit" />
扫描到即可;
@ControllerAdvice 可以实现三个方面的功能:
- 1 全局数据绑定 @ModelAttribute
- 2 全局数据预处理 @InitBinder
1和2 请查看 https://www.cnblogs.com/lenve/p/10748453.html 这篇文章 - 3 全局异常处理 @ExceptionHandler
本文主要介绍全局异常处理
在之前的SpringMVC项目中,经常是用try…catch…来进行异常处理,实例代码如下:
@RequestMapping(value="/testException")
public RestBaseResult<Object> testExceptionLogin(@RequestParam String name,@RequestParam String password) {
RestBaseResult<Object> result = new RestBaseResult<Object>();
try {
if (!password.equals("123456")) {
result.setCode(100011);
result.setMsg("密码错误");
}
doSomething..........
sdOrderService.testExceptionLogin(name,password);
} catch (Exception e) {
logger.error("----controller.login.errorInfo>:{}",e);
result.setCode(-1);
result.setMsg("服务器内部错误");
}
return result;
}
try…catch…异常的缺点: 代码冗余,不利于维护,在拦截器中的定义的错误消息无法返回到页面
接下来介绍一个@ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理
一、优缺点
优点:
异常进行统一处理;
减少模板代码,减少编码量,提升扩展性和可维护性;
Interceptor(拦截器)层的异常也可以被处理
缺点:
只能处理 未捕获(往外抛)的异常,对于 过滤器(Filter),Spring 框架层的异常,就无能为力了。
为了测试,所有的类都定义在exception包下:
过滤器和拦截器的配置为贴出来了
/**
* 全局异常处理类
*
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR) //自定义浏览器返回状态码,通常不需要自定义
@ExceptionHandler()
public @ResponseBody ExceptionResult handleException(Exception e,
HttpServletRequest request, HttpServletResponse response) {
// TODO 记录log日志
e.printStackTrace();
logger.error("---------GlobalExceptionHandler.Exception.errorinfo>:{}",e);
ExceptionResult result = new ExceptionResult();
result.setData(e.getMessage());
return result;
}
@ExceptionHandler({BusinessException.class})
public @ResponseBody ExceptionResult handleServiceException(BusinessException e,
HttpServletRequest request, HttpServletResponse response) {
// TODO 记录log日志
e.printStackTrace();
logger.error("---------GlobalExceptionHandler.BusinessException.errorinfo>:{}",e);
ExceptionResult result = new ExceptionResult();
result.setCode(e.getCode());
result.setMsg(e.getMsg());
result.setData(e.getMessage());
return result;
}
@ExceptionHandler({NullPointerException.class})
public @ResponseBody ExceptionResult handleServiceException(NullPointerException e,
HttpServletRequest request, HttpServletResponse response) {
// 记录log日志
e.printStackTrace();
logger.error("---------GlobalExceptionHandler.NullPointerException.errorinfo>:{}",e);
ExceptionResult result = new ExceptionResult();
result.setMsg("空指针异常");
result.setData(e.getMessage());
return result;
}
}
/**
* -------------------BusinessException.java----------------------------------------------------
* 业务异常
*/
@Data
@EqualsAndHashCode(callSuper=false)
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
/** 异常码 */
private int code;
/** 异常提示内容 */
private String msg;
public BusinessException() {
super();
}
public BusinessException(String msg) {
super(msg);
}
public BusinessException(int code,String msg) {
this.code = code;
this.msg = msg;
}
}
/**
* -------------------ExceptionCode.java----------------------------------------------------
* 自定义异常码
*/
public class ExceptionCode {
/** 用户名错误 */
public static final int USERNAME_ERROR = 100010;
/** 密码错误 */
public static final int PWD_ERROR = 100011;
/**
* 根据code获取MSG
* @param code
* @return
*/
public static String getMsgByCode(int code) {
String msg = "Server internal error";
switch (code) {
case USERNAME_ERROR:
msg = "用户名错误";
break;
case PWD_ERROR:
msg = "密码错误";
break;
default:
break;
}
return msg;
}
}
/**
* --------------------------异常结果----------------------------
*
*/
@Data
public class ExceptionResult implements Serializable{
private static final long serialVersionUID = 1L;
// -- 返回代码定义 --//
// 按项目的规则进行定义, 比如4xx代表客户端参数错误,5xx代表服务端业务错误等.
public static final int SUCCESS = 0;
public static final int PARAMETER_ERROR = -1;
public static final int SYSTEM_ERROR = 500;
public static final String SYSTEM_SUCCESS_MESSAGE = "operate success";
public static final String SYSTEM_ERROR_MESSAGE = "Runtime unknown internal error.";
// -- WsResult基本属性 --//
private int code = PARAMETER_ERROR;
private String msg = SYSTEM_ERROR_MESSAGE;
private String currentTime = DateUtils.convertDateToString(DateUtils.DATETIME, new Date());
private Object data;
}
/**
* ---------------过滤器
*/
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String name = request.getParameter("name");
if (!name.equals("admin")) {
throw new BusinessException(222, "用户名错误");
}
chain.doFilter(request, response);// 调用接口
}
@Override
public void destroy() {}
}
/**
* ---------------拦截器
*/
public class TestInterceptor implements HandlerInterceptor {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Map<String,String[]> map = request.getParameterMap();
Map<String,String> param = new HashMap<String,String>();
for(Entry<String, String[]> entry : map.entrySet()) {
String [] values = entry.getValue();
String key = entry.getKey();
if(values[0] != null) {
param.put(key, values[0]);
}
}
String sign = param.get("sign");
if (!sign.equals("aaaaaaaaa")) {
throw new BusinessException(333, "签名错误");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
测试的controller和service如下
@Controller
@RequestMapping("/user")
public class UserController extends BaseController{
@Autowired
private UserService userService;
@RequestMapping(value="/testException")
public RestBaseResult<Object> testExceptionLogin(@RequestParam String name,@RequestParam String password,Model model) {
Map<String, Object> map = model.asMap();
logger.info("------>:map{}",map);
RestBaseResult<Object> result = new RestBaseResult<Object>();
if (password.equals("123456"))
throw new BusinessException(ExceptionCode.PWD_ERROR,ExceptionCode.getMsgByCode(ExceptionCode.PWD_ERROR));
userService.testExceptionLogin(name,password);
return result;
}
}
----------------service----------------------
@service
public class UserServiceImpl implements UserService {
@Override
public void testExceptionLogin(String name,String pwd) {
User user = userDao.selectByName(name);
if (user == null) {
throw new BusinessException(ExceptionCode.USERNAME_ERROR,ExceptionCode.getMsgByCode(ExceptionCode.USERNAME_ERROR));
}
}
}
通过Post满工具测试的结果如下图:
处理Filter中定义的异常无法被@ExceptionHandler处理,其他的都可以,有兴趣的可以试试