SSM框架全局捕获异常

我们都知道,项目有两种异常,一种是ERROR,一种是Exception,ERROR导致项目直接崩盘,无法运行,且不能捕获,Exception可以捕获且不影响项目运行,今天要做的就是捕获Exception,当前项目开发使用SSM框架,我原本使用的方法是Controller控制层每一个类每一个方法都有一个try-catch捕获当前方法异常,虽然这种可用,但是会有几个问题:

向前端如何返回异常?
即如何保证发生异常时向前端返回的数据统一,因为是不同人员开发,很可能A人员直接将异常扔给前端,而B人员将异常处理之后返回前端一个标识
如何记录异常信息?
我们要考虑到系统中每一处代码都有发生异常的可能性(在这里先仅考虑Controller层),即使我们编写异常记录的工具类,难道我们在每处发生异常的地方都调用吗?(不谈资源浪费的情况下,如何确保每个开发人员记录格式相同?如何确保每处Exception都会被记录?)
基于以上两点,我基于SSM框架做了一个Exception的全局异常捕获,邮件提醒(亲测有效)
在这里即使用GlobalExceptionHandler类捕获控制层的异常,代码如下:

package com.single.cong.exception;

import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import lombok.extern.slf4j.Slf4j;
/**
 * 全局捕获异常
 */
@Slf4j
@ControllerAdvice(basePackages = "com.single.cong.controller")
public class GlobalExceptionHandler {

	@Autowired
	private JavaMailSender javaMailSender;

	/**
	 * 异常信息转化为String类型,等同于e.printStackTrace()输出参数
	 * 
	 * @param t
	 *            异常
	 * @return 异常详细信息
	 * @author single-聪
	 * @date 2019年4月15日
	 * @version 1.0.0
	 */
	public String getTrace(Throwable t) {
		StringWriter stringWriter = new StringWriter();
		PrintWriter writer = new PrintWriter(stringWriter);
		t.printStackTrace(writer);
		StringBuffer buffer = stringWriter.getBuffer();
		SimpleMailMessage mailMessage = new SimpleMailMessage();
		mailMessage.setFrom("123456789@qq.com");
		mailMessage.setTo("1234567890@qq.com");
		mailMessage.setSubject("系统Bug,及时修复");
		mailMessage.setText(buffer.toString());
		javaMailSender.send(mailMessage);
		return buffer.toString();
	}
	
	/**
	 * 全局异常捕获,暂时区分两种异常类型,所有运行时异常统一在此方法中
	 * 
	 * @param e
	 *            异常
	 * @author single-聪
	 * @date 2019年4月15日
	 * @version 1.0.0
	 * 
	 */
	@ExceptionHandler(RuntimeException.class)
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	public void runtimeException(Exception e) {
		log.info("全局捕获运行时异常,同时写入日志文件");
		e.printStackTrace();
		log.error(getTrace(e));
	}

	/**
	 * 账号权限信息不足
	 * 
	 * @param e
	 *            异常
	 * @author single-聪
	 * @date 2019年4月15日
	 * @version 1.0.0
	 * 
	 */
	@ExceptionHandler(AccessDeniedException.class)
	@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
	public void accessDeniedException(Exception e) {
		log.info("账号权限信息不足");
		e.printStackTrace();
		log.error(getTrace(e));
	}
}

@ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上,basePackages代表指定包下面的方法,可以使用不同的类处理不同的异常。
@ExceptionHandler(RuntimeException.class)用于全局处理控制器里的异常,在这里我们只处理运行时异常以及权限验证异常。
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)返回状态码
参数里面加上Exception之后就可以使用e.printStackTrace()在控制台打印出异常,在这里将打印出的异常存储进日志文件(SpringBoot使用logback)方便查看,这样我们就不需要在每个控制层方法里面写多余的代码了。

注意,邮件发送部分需要更换成自己的账号信息,同时需要配置key。

在使用这个全局方法之前,我的控制层代码如下:

// 删除
@RequestMapping(value = "/delete.sose", consumes = "application/json;charset=UTF-8")
public Map<String, Object> delete(@RequestBody String c) throws ParseException {
	java.util.Map<String, Object> map = new HashMap<>();
	try {
		// 获取前端传回删除数据Id
		JSONObject strj = new JSONObject(c);
		evolveService.delete(strj.getInt("id"));
		map.put("info", "success");
	} catch (Exception e) {
		e.printStackTrace();
		map.put("info", e);
	}
	return map;
}

  使用异常捕获机制之后,只要你能够将系统中的异常罗列出来,那么就可以根据异常类型决定返回指定的状态码,前端只处理状态码为200的数据,一旦状态码不是200就可以认为服务器端数据处理失败。至于具体的错误信息需不需要返回前端可以根据业务决定。

 

posted @ 2020-12-09 15:40  奥兰王子  阅读(307)  评论(0编辑  收藏  举报