经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Spring Boot 2.x(六):优雅的统一返回值
来源:cnblogs  作者:Vi的技术博客  时间:2018/12/28 9:52:04  对本文有异议

为什么要统一返回值

在我们做后端应用的时候,前后端分离的情况下,我们经常会定义一个数据格式,通常会包含codemessagedata这三个必不可少的信息来方便我们的交流,下面我们直接来看代码

ReturnVO

  1. package indi.viyoung.viboot.util;
  2. import java.util.Properties;
  3. /**
  4. * 统一定义返回类
  5. *
  6. * @author yangwei
  7. * @since 2018/12/20
  8. */
  9. public class ReturnVO {
  10. private static final Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + "/viboot-common/src/main/resources/response.properties");
  11. /**
  12. * 返回代码
  13. */
  14. private String code;
  15. /**
  16. * 返回信息
  17. */
  18. private String message;
  19. /**
  20. * 返回数据
  21. */
  22. private Object data;
  23. public Object getData() {
  24. return data;
  25. }
  26. public void setData(Object data) {
  27. this.data = data;
  28. }
  29. public String getMessage() {
  30. return message;
  31. }
  32. public void setMessage(String message) {
  33. this.message = message;
  34. }
  35. public String getCode() {
  36. return code;
  37. }
  38. public void setCode(String code) {
  39. this.code = code;
  40. }
  41. /**
  42. * 默认构造,返回操作正确的返回代码和信息
  43. */
  44. public ReturnVO() {
  45. this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));
  46. this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));
  47. }
  48. /**
  49. * 构造一个返回特定代码的ReturnVO对象
  50. * @param code
  51. */
  52. public ReturnVO(ReturnCode code) {
  53. this.setCode(properties.getProperty(code.val()));
  54. this.setMessage(properties.getProperty(code.msg()));
  55. }
  56. /**
  57. * 默认值返回,默认返回正确的code和message
  58. * @param data
  59. */
  60. public ReturnVO(Object data) {
  61. this.setCode(properties.getProperty(ReturnCode.SUCCESS.val()));
  62. this.setMessage(properties.getProperty(ReturnCode.SUCCESS.msg()));
  63. this.setData(data);
  64. }
  65. /**
  66. * 构造返回代码,以及自定义的错误信息
  67. * @param code
  68. * @param message
  69. */
  70. public ReturnVO(ReturnCode code, String message) {
  71. this.setCode(properties.getProperty(code.val()));
  72. this.setMessage(message);
  73. }
  74. /**
  75. * 构造自定义的code,message,以及data
  76. * @param code
  77. * @param message
  78. * @param data
  79. */
  80. public ReturnVO(ReturnCode code, String message, Object data) {
  81. this.setCode(code.val());
  82. this.setMessage(message);
  83. this.setData(data);
  84. }
  85. @Override
  86. public String toString() {
  87. return "ReturnVO{" +
  88. "code='" + code + '\'' +
  89. ", message='" + message + '\'' +
  90. ", data=" + data +
  91. '}';
  92. }
  93. }

在这里,我提供了几个构造方法以供不同情况下使用。代码的注释已经写得很清楚了,大家也可以应该看的比较清楚~

ReturnCode

细心的同学可能发现了,我单独定义了一个ReturnCode枚举类用于存储代码和返回的Message:

  1. package indi.viyoung.viboot.util;
  2. /**
  3. * @author yangwei
  4. * @since 2018/12/20
  5. */
  6. public enum ReturnCode {
  7. /** 操作成功 */
  8. SUCCESS("SUCCESS_CODE", "SUCCESS_MSG"),
  9. /** 操作失败 */
  10. FAIL("FAIL_CODE", "FAIL_MSG"),
  11. /** 空指针异常 */
  12. NullpointerException("NPE_CODE", "NPE_MSG"),
  13. /** 自定义异常之返回值为空 */
  14. NullResponseException("NRE_CODE", "NRE_MSG");
  15. private ReturnCode(String value, String msg){
  16. this.val = value;
  17. this.msg = msg;
  18. }
  19. public String val() {
  20. return val;
  21. }
  22. public String msg() {
  23. return msg;
  24. }
  25. private String val;
  26. private String msg;
  27. }

这里,我并没有将需要存储的数据直接放到枚举中,而是放到了一个配置文件中,这样既可以方便我们进行相关信息的修改,并且阅读起来也是比较方便。

  1. SUCCESS_CODE=2000
  2. SUCCESS_MSG=操作成功
  3. FAIL_CODE=5000
  4. FAIL_MSG=操作失败
  5. NPE_CODE=5001
  6. NPE_MSG=空指针异常
  7. NRE_CODE=5002
  8. NRE_MSG=返回值为空

注意,这里的属性名和属性值分别与枚举类中的value和msg相对应,这样,我们才可以方便的去通过I/O流去读取。

这里需要注意一点,如果你使用的是IDEA编辑器,需要修改以下的配置,这样你编辑配置文件的时候写的是中文,实际上保存的是ASCII字节码。

下面,来看一下读取的工具类:

  1. package indi.viyoung.viboot.util;
  2. import java.io.*;
  3. import java.util.Iterator;
  4. import java.util.Properties;
  5. /**
  6. * 读取*.properties中的属性
  7. * @author vi
  8. * @since 2018/12/24 7:33 PM
  9. */
  10. public class ReadPropertiesUtil {
  11. public static Properties getProperties(String propertiesPath){
  12. Properties properties = new Properties();
  13. try {
  14. InputStream inputStream = new BufferedInputStream(new FileInputStream(propertiesPath));
  15. properties.load(inputStream);
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. return properties;
  20. }
  21. }

这里我直接写了一个静态的方法,传入的参数是properties文件的位置,这样的话,本文最初代码中的也就得到了解释。

  1. private static final Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + "/viboot-common/src/main/resources/response.properties");

使用ReturnVO

  1. @RequestMapping("/test")
  2. public ReturnVO test(){
  3. try {
  4. //省略
  5. //省略
  6. } catch (Exception e) {
  7. e.printStackTrace();
  8. }
  9. return new ReturnVO();
  10. }

下面我们可以去访问这个接口,看看会得到什么:

但是,现在问题又来了,因为try...catch...的存在,总是会让代码变得重复度很高,一个接口你都至少要去花三到十秒去写这个接口,如果不知道编辑器的快捷键,更是一种噩梦。我们只想全心全意的去关注实现业务,而不是花费大量的时间在编写一些重复的"刚需"代码上。

使用AOP进行全局异常的处理

(这里,我只是对全局异常处理进行一个简单的讲解,后面也就是下一节中会详细的讲述)

  1. /**
  2. * 统一封装返回值和异常处理
  3. *
  4. * @author vi
  5. * @since 2018/12/20 6:09 AM
  6. */
  7. @Slf4j
  8. @Aspect
  9. @Order(5)
  10. @Component
  11. public class ResponseAop {
  12. private static final Properties properties = ReadPropertiesUtil.getProperties(System.getProperty("user.dir") + "/viboot-common/src/main/resources/response.properties");
  13. /**
  14. * 切点
  15. */
  16. @Pointcut("execution(public * indi.viyoung.viboot.*.controller..*(..))")
  17. public void httpResponse() {
  18. }
  19. /**
  20. * 环切
  21. */
  22. @Around("httpResponse()")
  23. public ReturnVO handlerController(ProceedingJoinPoint proceedingJoinPoint) {
  24. ReturnVO returnVO = new ReturnVO();
  25. try {
  26. //获取方法的执行结果
  27. Object proceed = proceedingJoinPoint.proceed();
  28. //如果方法的执行结果是ReturnVO,则将该对象直接返回
  29. if (proceed instanceof ReturnVO) {
  30. returnVO = (ReturnVO) proceed;
  31. } else {
  32. //否则,就要封装到ReturnVO的data中
  33. returnVO.setData(proceed);
  34. }
  35. } catch (Throwable throwable) {
  36. //如果出现了异常,调用异常处理方法将错误信息封装到ReturnVO中并返回
  37. returnVO = handlerException(throwable);
  38. }
  39. return returnVO;
  40. }
  41. /**
  42. * 异常处理
  43. */
  44. private ReturnVO handlerException(Throwable throwable) {
  45. ReturnVO returnVO = new ReturnVO();
  46. //这里需要注意,返回枚举类中的枚举在写的时候应该和异常的名称相对应,以便动态的获取异常代码和异常信息
  47. //获取异常名称的方法
  48. String errorName = throwable.toString();
  49. errorName = errorName.substring(errorName.lastIndexOf(".") + 1);
  50. //直接获取properties文件中的内容
  51. returnVO.setMessage(properties.getProperty(ReturnCode.valueOf(errorName).msg()));
  52. returnVO.setCode(properties.getProperty(ReturnCode.valueOf(errorName).val()));
  53. return returnVO;
  54. }
  55. }

如果,我们需要在每一个项目中都可以这么去做,需要将这个类放到一个公用的模块中,然后在pom中导入这个模块

  1. <dependency>
  2. <groupId>indi.viyoung.course</groupId>
  3. <artifactId>viboot-common</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

这里需要注意一点,必须保证你的切点的正确书写!!否则就会导致切点无效,同时需要在启动类中配置:

  1. @ComponentScan(value = "indi.viyoung.viboot.*")

导入的正是common包下的所有文件,以保证可以将ResponseAop这个类加载到Spring的容器中。

下面我们来测试一下,访问我们经过修改后的编写的findAll接口:

  1. @RequestMapping("/findAll")
  2. public Object findAll(){
  3. return userService.list();
  4. }

PS:这里我将返回值统一为Object,以便数据存入data,实际类型应是Service接口的返回类型。如果没有返回值的话,那就可以new一个ReturnVO对象直接通过构造方法赋值即可。关于返回类型为ReturnVO的判断,代码中也已经做了特殊的处理,并非存入data,而是直接返回。


下面,我们修改一下test方法,让他抛出一个我们自定义的查询返回值为空的异常:

  1. @RequestMapping("/test")
  2. public ReturnVO test(){
  3. throw new NullResponseException();
  4. }

下面,我们再来访问以下test接口:

可以看到,正如我们properties中定义的那样,我们得到了我们想要的消息。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

源码可以去github或者码云上进行下载,后续的例子都会同步更新。

云撸猫

原文链接:https://www.cnblogs.com/viyoung/p/10188456.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号