python切面异常处理_Spring项目中优雅的异常处理

Spring项目中优雅的异常处理

前言

如今的Java Web项目多是以 MVC 模式构建的,通常我们都是将 Service 层的异常统一的抛出,包括自定义异常和一些意外出现的异常,以便进行事务回滚,而 Service 的调用者 Controller 则承担着异常处理的责任,因为他是与 Web 前端交互的最后一道防线,如果此时还不进行处理则用户会在网页上看到一脸懵逼的

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4

at cn.keats.TestAdd.main(TestAdd.java:20)

这样做有以下几点坏处:

用户体验很不友好,可能用户会吐槽一句:这是什么XX网站。然后不再访问了

如果这个用户是同行,他不仅看到了项目代码的结构,而且看到抛出的是这么低级的索引越界异常,会被人家看不起

用户看到网站有问题,打电话给客服,客服找到产品,产品叫醒正在熟睡/打游戏的你。你不仅睡不好游戏打不了还得挨批评完事改代码

哎,真惨。因此一般我们采用的方法会是像这样:

异常处理

一般的Controller处理

Service代码如下:

@Service

public class DemoService{

public String respException(String param){

if(StringUtils.isEmpty(param)){

throw new MyException(ExceptionEnum.PARAM_EXCEPTION);

}

int i = 1/0;

return "你看不见我!";

}

}

Controller代码如下:

@RestController

public class DemoController{

@Autowired

private DemoService demoService;

@PostMapping("respException")

public Result respException(){

try {

return Result.OK(demoService.respException(null));

} catch (MyException e){

return Result.Exception(e, null);

}

catch (Exception e) {

return Result.Error();

}

}

}

如果此时发送如下的请求:

http://localhost/respException

服务器捕捉到自定义的异常 MyException,而返回参数异常的Json串:

{

"code": 1,

"msg": "参数异常",

"data": null

}

而当我们补上参数:

http://localhost/respException?param=zhangsan

则服务器捕捉到 by zero 异常,会返回未知错误到前端页面

{

"code": -1,

"msg": "未知错误",

"data": null

}

这样就会在一定程度上规避一些问题,例如参数错误就可以让用户去修改其参数,当然这一般需要前端同学配合做页面的参数校验,必传参数都有的时候再向服务器发送请求,一方面减轻服务器压力,一方面将问题前置节省双方的时间。但是这样写有一个坏处就是所有的Controller方法中关于异常的部分都是一样的,代码非常冗余。且不利于维护,而且一些不太熟悉异常机制的同学可能会像踢皮球一样将异常抓了抛,抛完又抓回来,闹着玩呢。。。(笔者就曾经接手过一个跑路同学的代码这样处理异常,那简直是跟异常捉迷藏呢!可恨)我们在Service有全局事务处理,在系统中可以有全局的日志处理,这些都是基于Spring 的一大杀器:AOP(面向切面编程) 实现的,AOP是什么呢?

AOP

AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。如果说我们常用的OOP思想是从上到下执行业务流程的话,AOP就相当于在我们执行业务的时候横切一刀,如下图所示:

而Advice(通知)是AOP思想中重要的一个术语,分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)五种。具体通知所表示的意义我这里不多赘述,网上关于Spring核心原理的讲解都会提及。而我们熟知的 Service 事务处理其实就是基于AOP AfterThrowing 通知实现的事务回滚。我们自定义的日志处理也可以根据不同的需求定制不同的通知入口。那既然如此,我们为何不自定义一个全局异常处理的切面去简化我们的代码呢?别急,且继续向下看。

优雅的处理异常

Spring 在 3.2 版本已经为我们提供了该功能: @ControllerAdvice 注解。此注解会捕捉Controller层抛出的异常,并根据 @ExceptionHandler 注解配置的方法进行异常处理。下面是一个示例工程,主要代码如下:

Result类:

此 Result 采用泛型的方式,便于在 Swagger 中配置方法的出参。使用静态工厂方法是的对象的初始化更加见名只意。对于不存在共享变量问题的 Error 对象,采用双重校验锁懒汉单例模式来节省服务器资源(当然最好还是整个项目运行中一直没有初始化它让人更加舒服。)

package cn.keats.util;

import cn.keats.exception.MyException;

import lombok.Data;

/**

* 功能:统一返回结果,直接调用对应的工厂方法

*

* @author Keats

* @date 2019/11/29 18:20

*/

@Data

public class Result{

private Integer code;

private String msg;

private T data;

/**

* 功能:响应成功

*

* @param data 响应的数据

* @return woke.cloud.property.transformat.Result

* @author Keats

* @date 2019/11/30 8:54

*/

public static Result OK(T data){

return new Result<>(0, "响应成功", data);

}

private static Result errorResult;

/**

* 功能:返回错误,此错误不可定制,全局唯一。一般是代码出了问题,需要修改代码

*

* @param

* @return Result

* @author Keats

* @date 2019/11/30 8:55

*/

public static Result Error(){

if(errorResult == null){

synchronized (Result.class){

if(errorResult == null){

synchronized (Result.class){

errorResult = new Result<>(-1, "未知错误", null);

}

}

}

}

return errorResult;

}

/**

* 功能:返回异常,直接甩自定义异常类进来

*

* @param e 自定义异常类

* @param data 数据,如果没有填入 null 即可

* @return woke.cloud.property.transformat.Result

* @author Keats

* @date 2019/11/30 8:55

*/

public static Result Exception(MyException e, T data){

return new Result<>(e.getCode(), e.getMsg(), data);

}

/**

* 功能:为了方便使用,使用静态工厂方法创建对象。如需新的构造方式,请添加对应的静态工厂方法

*

* @author Keats

* @date 2019/11/30 8:56

*/

private Result(Integer code, String msg, T data){

this.code = code;

this.msg = msg;

this.data = data;

}

}

自定义异常类:

package cn.keats.exception;

import lombok.Getter;

/**

* 功能:系统自定义异常类。继承自RuntimeException,方便Spring进行事务回滚

*

* @author Keats

* @date 2019/11/29 18:50

*/

@Getter

public class MyException extends RuntimeException{

private Integer code;

private String msg;

public MyException(ExceptionEnum eEnum){

this.code = eEnum.getCode();

this.msg = eEnum.getMsg();

}

}

异常代码枚举类:

package cn.keats.exception;

import lombok.AllArgsConstructor;

import lombok.Getter;

/**

* 功能:异常枚举

*

* @author Keats

* @date 2019/11/29 18:49

*/

@Getter

@AllArgsConstructor

public enum ExceptionEnum {

PARAM_EXCEPTION(1,"参数异常"),

USER_NOT_LOGIN(2,"用户未登录"),

FILE_NOT_FOUND(3,"文件不存在,请重新选择");

private Integer code;

private String msg;

}

异常切面:

其中 @RestControllerAdvice 是spring 4.3 添加的新注解,是 @ControllerAdvice 和 @ResponseBody 的简写方式,类似与 @RestController 与 @Controller 的关系

package cn.keats.advice;

import cn.keats.exception.MyException;

import cn.keats.util.Result;

import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestControllerAdvice;

/**

* 功能:全局异常处理器,Controller异常直接抛出

*

* @return

* @author Keats

* @date 2019/11/30 10:28

*/

@Slf4j

@RestControllerAdvice

public class ExceptionAdvice{

/**

* 功能:其余非预先规避的异常返回错误

*

* @param e

* @return woke.cloud.property.transformat.Result

* @author Keats

* @date 2019/11/30 10:08

*/

@ExceptionHandler(value = Exception.class)

@ResponseBody

public Result ResponseException(Exception e){

log.error("未知错误,错误信息:", e);

return Result.Error();

}

/**

* 功能:捕捉到 MyException 返回对应的消息

*

* @param e

* @return woke.cloud.property.transformat.Result

* @author Keats

* @date 2019/11/30 10:07

*/

@ExceptionHandler(value = MyException.class)

@ResponseBody

public Result myException(MyException e){

log.info("返回自定义异常:异常代码:" + e.getCode() + "异常信息:" + e.getMsg());

return Result.Exception(e, null);

}

}

此时的 Controller 方法可以这样写:

package cn.keats.controller;

import cn.keats.service.DemoService;

import cn.keats.util.Result;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class DemoController{

@Autowired

private DemoService demoService;

@PostMapping("respException")

public Result respException(String param) throws Exception{

return Result.OK(demoService.respException(param));

}

@PostMapping("respError")

public Result respError() throws Exception{

return Result.OK(demoService.respException(null));

}

}

省略的大部分的异常处理代码,使得我们只需要关注业务,一方面提高了代码质量,可阅读性,另一方面也提高了我们的开发速度。美哉!

启动项目,进行测试没有问题。

我是 Keats,一个热爱技术的程序员,鉴于技术有限,如果本文有什么纰漏或者兄台还有其他更好的建议/实现方式,欢迎留言评论,谢谢您!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/527047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu查看服务器信息,查看Ubuntu服务器的版本信息

Docker on YARN在Hulu的实现这篇文章是我来Hulu这一年做的主要工作,结合当下流行的两个开源方案Docker和YARN,提供了一套灵活的编程模型,目前支持DAG编程模型,将会支持长服务编程模型. 基于Voidbox,开发者可以很 ...使用angularJS遇见的一些问题的解决方案1. angularJS的$http.…

方法 手写promise_JS探索-手写Promise

无意间在知乎上刷到Monad这个概念&#xff0c;去了解了一下&#xff0c;前端的Promise就是一种Monad模式&#xff0c;所以试着学习一下手写一个Promise.本文内容主要参考于只会用&#xff1f;一起来手写一个合乎规范的Promise​www.jianshu.comPromise是什么所谓Promise&#x…

dcs服务器性能指标,第6章DCS的性能指标.PDF

第6章DCS的性能指标第 6 章 DCS 的性能指标随着 DCS 的广泛应用&#xff0c;对 DCS 的可靠性要求也越来越高&#xff0c;因此正确评价 DCS 的可靠性越来越显得重要。DCS 的可靠与否&#xff0c;通常包含两重意思&#xff1a;一是指产品在规定的时间内&#xff0c;完成规定功能的…

12如何隐藏dock栏_一键隐藏 iPhone 刘海和底部 Dock 栏,简洁又好看

技能&#xff1a; 隐藏刘海和底部dock 栏难度系数&#xff1a;2颗星适用系统&#xff1a;iOS 13(部分非iOS13也适用)最近&#xff0c;小雷打开手机&#xff0c;看到最多的关键词&#xff0c;就是&#xff1a;iOS13又双叒叕更新了。。。而且也看到不止一位网友晒这个有趣的新功能…

湖南hp服务器虚拟化解决方案,HP刀片服务器虚拟化整合解决方案-20210729062411.docx-原创力文档...

HP刀片办事器 虚拟化整合解决方案22 -目录TOC \o "1-3" \h \u 1&#xff0e;办事器硬件整合 - 2 -什么是办事器整合 - 2 -1.2 为何要做办事器整合 - 2 -2&#xff0e;刀片办事器的观点 - 3 -3&#xff0e;刀片办事器如何整合IT资源 - 4 -3.1 新一代数据中心对IT设施系…

宝塔php安装那个合_使用宝塔面板安装nextcloud | 启用本地存储 | 安装smbclient

宝塔面板安装nextcloud | 启用本地存储使用宝塔面板搭建nextcloud服务后&#xff0c;在设置外部存储时总是无法启用本地存储。问题1&#xff1a;提示&#xff1a;“smbclient” 未安装。无法挂载 "SMB / CIFS", "SMB / CIFS 使用 OC 登录信息"。请联系管理…

springbboot加密打包_Spring Boot 配置 Security 密码加密

依赖org.springframework.bootspring-boot-starter-security注入beanSpringBootApplicationpublic class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class, args);}Beanpublic BCryptPasswordEncoder encoding(){retur…

mysql groupby 拼接_mysql groupby 字段合并问题(group_concat)

在我们的日常mysql查询中&#xff0c;我们可能会遇到这样的情况&#xff1a;对表中的所有记录进行分类&#xff0c;并且我需要得到每个分类中某个字段的全部成员。上面的话&#xff0c;大家看起来可能不太好懂&#xff0c;下面举一个例子来给大家说明。现在我们有一张表&#x…

mysql 基础教程 西泽 好吗_Mysql基础教程

往期推荐SQL语句分类DDL&#xff1a;数据定义语言&#xff0c;用来定义数据库对象&#xff1a;库、表、列等DML&#xff1a;数据操作语言&#xff0c;用来定义数据库记录(数据)DCL&#xff1a;数据控制语言&#xff0c;用来定义访问权限和安全级别&#xff1b;DQL&#xff1a;数…

mysql 字符集测试_关于字符集的测试报告_MySQL

nls_lang用于设置客户端的字符集。影响sqlplus界面的语言。regedit:hkey_local_machine/software/oracle/home0nls_lang键值1、取值为‘US7ASCII或为空2、取值为‘simplified chinese_china.ZHS16GBKnls_characterset用于设置数据库内部字符集&#xff0c;决定数据记录保存的格…

mysql数学函数名_MYSQL 常见数学函数说明

摘要:下文讲述MySQL常见数学函数的说明&#xff0c;如下所示:mysql > select round(2.34);-------------| round(2.34) |-------------| 2 |-------------1 row in set (0.00 sec)mysql > select round(2.34,1);---------------| round(2.34,1) |---------------| 2.3 |-…

mysql聚集索引 myisam_一句话说清聚集索引和非聚集索引以及MySQL的InnoDB和MyISAM

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。https://blog.csdn.net/21aspnet/article/details/89303988聚集索引和非聚集索引以及MySQL的InnoDB和MyISAM经常遇到有人向我咨询这个问题&#xff0c;其实呢&#xff0c;网上帖子很多&#xff0c;也…

mysql for loop_MySQL循环语句 - Linux就该这么学的个人空间 - OSCHINA - 中文开源技术交流社区...

mysql常见的三种循环方式&#xff1a;while、repeat和loop循环。还有一种goto&#xff0c;不推荐使用。1.while循环-- 设置mysql分隔符为//&#xff0c;也就意味着&#xff0c;当遇到下一个//时&#xff0c;整体执行SQL语句DELIMITER //DROP PROCEDURE if EXISTS ‘test’; # 如…

mysql的存储过程放在哪里_mysql存储过程求解,错误在哪里?

DELIMITER $$ALTER PROCEDURE sp_test(vdnId CHAR(2),startTimeStr CHAR(8))BEGINDECLARE v_startTime DATETIME;DECLARE v_endTime DATETIME;DECLARE v_VDNName CHAR(2);DECLARE v_sql VARCHAR(1000);/*判断输入参数的合法性*/SELECT t.VDN_name INTO v_VDNName FROM gdmdw.t_…

ue4插件导入导出_Blender到UE4的无缝衔接

Hello . 大家好本文给大家介绍一下Send To Unreal插件我是Vee1简介Send To Unreal是Epic官方开发的用于Blender和UE4快速同步的插件&#xff0c;支持静态物体、骨骼物体、动画等等。省去了Blender导出-选择目录文件-UE4导入这个中间步骤&#xff0c;效率提升不是一般得多。插件…

mysql数据库管理系统模式_MYSQL命令行模式管理MySql的一点心得

MYSQL命令行模式管理MySql的一点心得MYSQL命令行模式管理MySql的一点心得MySql数据库是中小型网站后台数据库的首选,因为它对非商业应用是免费的.网站开发者可以搭建一个"LinuxApachePHPMySql"平台,这是一个最省钱的高效平台.在使用MySql进行开发时,MySql自带的文档对…

mysql点击计数器_MySql计数器,如网站点击数,如何实现高性能高并发的计数器功能...

MySql计数器&#xff0c;如网站点击数&#xff0c;如何实现高性能高并发的计数器功能Clicks: 5338 Date: 2014-03-29 23:30:42 Power By 李轩LaneTagMysql计数器高性能现在有很多的项目&#xff0c;对计数器的实现甚是随意&#xff0c;比如在实现网站文章点击数的时候&#xff…

python 微服务架构_微服务架构(Python)

在后端开发方面&#xff0c;Java的使用呢要远比Python广泛&#xff0c;所以Java的微服务框架非常流行&#xff0c;但Python的微服务框架却很少有人问津。在大多数需要微服务的场合下直接用Java的各种工具就可以解决问题&#xff0c;但如果业务代码使用Python写的&#xff0c;那…

opencv imread后做resizie_opencv第1课-加载、修改、保存图像

&#xff08;非原创&#xff0c;看课程自己做的笔记&#xff0c;防丢失放到个人空间的&#xff09;第1课-加载、修改、保存图像加载图像&#xff08;cv::imread&#xff09;修改图像&#xff08;cv::cvtColor&#xff09;保存图像&#xff08;cv::imwrite&#xff09;代码演示1…

php suse 安装mysql_SUSE Linux apache,Mysql,php,svn的安装

一 、安装apache1. tar -zxvf httpd-2.2.17.tar.gz2. cd httpd-2.2.173. sudo ./configure --prefix/space/soft/apache2 --enable-so --enable-dav --enable-dav-fs --enable-maintainer-mode --with-included-apr --enable-rewrite --enable-ssl --enable-proxy --enable-pro…