【SpringBoot】全局异常处理与发生异常时的邮件通知

                                    全局异常处理与发生异常时的邮件通知

一、前言

在任何一个SpringBoot项目中,Controlle层遍布异常捕获的代码,是不是觉得特别的不舒服呢。其实SpringBoot给我们提供了全局异常处理机制,使用ControllerAdvice与ExceptionHandler这两个注解即可。

我们现在的需求是,在任何Controller层出现代码,首先通过全局异常处理机制,捕获到该异常,然后用日志输出该异常出现的时间、异常种类、请求路径与参数等信息,最后通过邮件的方式通知开发者。


二、全局异常处理

ControllerAdvice:使用在全局异常处理类,当然也可以使用RestControllerAdvice,这样请求的响应是json类型的。

ExceptionHandler:标记该方法处理的异常类型,当发生的异常种类与ExceptionHandler中的值匹配时,便会进入处理方法中。

先把异常处理类贴出来

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    JavaMailSender javaMailSender;

    @ExceptionHandler(Exception.class)
    public Result<String> handlerException(Exception e, HttpServletRequest request) {
        Result<String> result = Result.exception(e, request);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String time = sdf.format(new Date());
        String exceptionStr = result.getMsg();
        String requestStr = result.getData();
        log.error("Time:{} Exception:{} Request:{}", time, exceptionStr, requestStr);

        sendSimpleMail(time, result);
        return result;
    }


    /**
     * 出现异常则发送邮件通知
     *
     * @param time
     * @param result
     */
    public void sendSimpleMail(String time, Result<String> result) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo("767638734@qq.com");
        message.setSubject("plm异常通知");
        StringBuffer text = new StringBuffer();
        text.append("发生时间: " + time);
        text.append("\n");
        text.append("异常种类: " + result.getMsg());
        text.append("\n");
        text.append("详细请求: " + result.getData());
        message.setText(text.toString());
        javaMailSender.send(message);
    }
}

用到的工具类Result

@Data
public class Result<T> {
    private int code;
    private String msg;
    private T data;


    public static <T> Result<T> success(String msg) {
        Integer code = ResultCodeEnum.SUCCESS.getCode();
        return result(code, msg, null);
    }

    public static <T> Result<T> success(String msg, T data) {
        Integer code = ResultCodeEnum.SUCCESS.getCode();
        return result(code, msg, data);
    }

    public static <T> Result<T> fail(String msg) {
        Integer code = ResultCodeEnum.FAILED.getCode();
        return result(code, msg, null);
    }

    public static <T> Result<T> fail(String msg, T data) {
        Integer code = ResultCodeEnum.FAILED.getCode();
        return result(code, msg, data);
    }

    public static Result<String> exception(Exception e, HttpServletRequest request) {
        Integer code = ResultCodeEnum.FAILED.getCode();
        String msg = e.toString();
        StringBuffer requestStr = new StringBuffer();
        //获取请求方法
        String requestMethod = request.getMethod();
        requestStr.append(requestMethod + " ");

        //请求路径
        StringBuffer requestURL = request.getRequestURL();
        requestStr.append(requestURL);
        if (requestMethod.equals("GET")) {
            //GET请求参数
            String queryString = request.getQueryString();
            if (null != queryString) {
                requestStr.append("?");
                requestStr.append(queryString);
            }
        } else {
            requestStr.append(" ");
            String parametersFromPost = RequestUtil.getParametersFromPost(request);
            requestStr.append(parametersFromPost);
        }

        return result(code, msg, requestStr.toString());
    }


    public static <T> Result<T> result(Integer code, String msg, T data) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

RequestUtil工具类

public class RequestUtil {

    /**
     * 获取POST请求中Body参数
     *
     * @param request
     * @return 字符串
     */
    public static String getParametersFromPost(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String line = "";
        StringBuilder sb = new StringBuilder();
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

}

编写一个Controller

@RestController
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    CategoryService categoryService;

    @GetMapping("/getAllCategory")
    public List<Category> getAllCategory() {
        return categoryService.getAllCategory();
    }

    @PostMapping("/add")
    public Result<Object> add(Category category) {
        throw new NullPointerException();
//        int affect = categoryService.add(category);
//        if (affect == 1) {
//            return Result.success("类目插入成功");
//        } else {
//            return Result.fail("类目插入失败");
//        }
    }

}

三、发送邮件

(1)导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

(2)配置yaml

spring:
  mail:
    host: smtp.qq.com
    username: 你的邮箱地址
    password: 从邮箱官网申请到的密码,不是登录邮箱的密码
    default-encoding: UTF-8

我用的是qq邮箱,需要开启SMTP协议,然后生成授权码


四、演示

在第二部分中,我们已经在Controller埋了一个空指针异常,现在我们请求该路径。

(1)请求

(2)日志显示

2019-09-04 16:15:55.339 ERROR 152984 --- [p-nio-80-exec-3] c.y.plm.handler.GlobalExceptionHandler   : 
Time:2019/09/04 16:15:55 
Exception:java.lang.NullPointerException 
Request:POST http://localhost/food/add {"name":"红烧鲫鱼"}

(3)邮件通知

大功告成!

 

 

全部评论
更多博客请移步https://blog.csdn.net/qq_33591903
点赞 回复 分享
发布于 2020-03-24 17:05

相关推荐

Tom哥981:让我来压力你!!!: 这份简历看着“技术词堆得满”,实则是“虚胖没干货”,槽点一抓一大把: 1. **项目描述是“技术名词报菜名”,没半分自己的实际价值** 不管是IntelliDoc还是人人探店,全是堆Redis、Elasticsearch、RAG这些时髦词,但你到底干了啥?“基于Redis Bitmap管理分片”是你写了核心逻辑还是只调用了API?“QPS提升至1500”是你独立压测优化的,还是团队成果你蹭着写?全程没“我负责XX模块”“解决了XX具体问题”,纯把技术文档里的术语扒下来凑字数,看着像“知道名词但没实际动手”的实习生抄的。 2. **短项目塞满超纲技术点,可信度直接***** IntelliDoc就干了5个月,又是RAG又是大模型流式响应又是RBAC权限,这堆活儿正经团队分工干都得小半年,你一个后端开发5个月能吃透这么多?明显是把能想到的技术全往里面塞,生怕别人知道你实际只做了个文件上传——这种“技术堆砌式造假”,面试官一眼就能看出水分。 3. **技能栏是“模糊词混子集合”,没半点硬核度** “熟悉HashMap底层”“了解JVM内存模型”——“熟悉”是能手写扩容逻辑?“了解”是能排查GC问题?全是模棱两可的词,既没对应项目里的实践,也没体现深度,等于白写;项目里用了Elasticsearch的KNN检索,技能栏里提都没提具体掌握程度,明显是“用过但不懂”的硬凑。 4. **教育背景和自我评价全是“无效信息垃圾”** GPA前10%这么好的牌,只列“Java程序设计”这种基础课,分布式、微服务这些后端核心课提都不提,白瞎了专业优势;自我评价那堆“积极认真、细心负责”,是从招聘网站抄的模板吧?没有任何和项目挂钩的具体事例,比如“解决过XX bug”“优化过XX性能”,纯废话,看完等于没看。 总结:这简历是“技术名词缝合怪+自我感动式凑数”,看着像“背了后端技术栈名词的应届生”,实则没干货、没重点、没可信度——面试官扫30秒就会丢一边,因为连“你能干嘛”都没说清楚。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务