开发日记8-优化接口使其更规范

痛点1

query接口有时候需要传入多个参数,是不是意味着query就一定要在传参增加很多个呢?

不是,你只需传入对象

痛点1解决方案

1 定义对象

java 复制代码
package com.wz.nls.business.req;

import lombok.Data;

@Data
public class DemoQueryReq {
    private String mobile;
}

2 修改接口

java 复制代码
    public List<Demo> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
        if (mobile != null) {
            criteria.andMobileNameEqualTo(mobile);
        }
        return demoMapper.selectByExample(example);
    }

3 测试

java 复制代码
GET http://localhost:18000/nls/query?mobile=Huawei
Accept: application/json

依然可以查到结果。

痛点2

返回的结果,类型五花八门,也不好统一维护

痛点2解决方案

在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/resp/CommonResp.java下

java 复制代码
package com.wz.nls.business.resp;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class CommonResp<T> {
    /**
     * 业务成功或失败
     */
    private boolean success=true;
    /**
     * 返回信息
     */
    private String message;
    /**
     * 返回泛型数据
     */
    private T cotent;
    public CommonResp(T cotent) {
        this.cotent = cotent;
    }
}

注意这里是使用泛型,而不是object

  • 泛型解决了 Object 的核心痛点 :编译期类型安全校验,杜绝ClassCastException运行时异常;
  • 泛型让代码更简洁:无需手动强制类型转换,减少冗余代码;
  • 泛型让代码更易读:类型语义清晰,团队协作更高效;
  • 泛型不丢失复用性:和 Object 一样适配任意类型,同时兼顾安全。

那么现在,接口查询获得的结果就要用它来包装

java 复制代码
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.26</version>
            </dependency>

或者,更简单的

java 复制代码
    @GetMapping("/query")
    public CommonResp<List<Demo>> query(DemoQueryReq req) {
        return new CommonResp<>(demoService.query(req));
    }

测试看看,现在很接近前端获取的数据了

痛点3

现在query查到的实体就是表结构,这样其实不太安全,也不具备拓展性,我们有时候需要增减属性

痛点3解决方案

首先在pom中引入依赖,最外层父pom,添加

java 复制代码
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.26</version>
            </dependency>

然后在business的子pom,添加

java 复制代码
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

然后更新maven

然后在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/resp/DemoQueryResp.java中引入

java 复制代码
package com.wz.nls.business.resp;

import lombok.Data;

@Data
public class DemoQueryResp {
    private Long id;
    private String mobileName;
}

这里可以直接屏蔽掉password

同步需改接口的类型,这里只有引入了hutool才能用上BeanUtil.copyToList

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
        if (mobile != null) {
            criteria.andMobileNameEqualTo(mobile);
        }
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }
}
java 复制代码
@RestController
public class TestController {
    @Resource
    private DemoService demoService;

    @GetMapping("/count")
    public CommonResp<Long> count() {
        return new CommonResp<>(demoService.count());
    }

    @GetMapping("/query")
    public CommonResp<List<DemoQueryResp>> query(DemoQueryReq req) {
        return new CommonResp<>(demoService.query(req));
    }
}

痛点4

异常太多如何管理

痛点4解决方案

增加统一异常处理

在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java下

java 复制代码
package com.wz.nls.business.controller;

import com.wz.nls.business.resp.CommonResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 统一异常处理、数据预处理等
 */
@Slf4j
@ControllerAdvice
public class ControllerExceptionHandler {

    /**
     * 所有异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(Exception e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage("系统出现异常,请联系管理员");
        return commonResp;
    }

}

它相当于一个拦截器,所有的异常都会统一在这里收束。

但是这里有个缺点,所有的异常都是统一的回复,即便我们在函数中抛出异常

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
//        if (mobile != null) {
//            criteria.andMobileNameEqualTo(mobile);
//        }
        if (StringUtils.isBlank(mobile)) {
            throw new RuntimeException("手机不能为空");
        }
        criteria.andMobileNameEqualTo(mobile);
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }

也无济于事,前端还是

而后端可以看到

这时候,我们可以针对性的根据异常来输出文本,在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java下

java 复制代码
package com.wz.nls.business.controller;

import com.wz.nls.business.resp.CommonResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 统一异常处理、数据预处理等
 */
@Slf4j
@ControllerAdvice
public class ControllerExceptionHandler {

    /**
     * 所有异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(Exception e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage("系统出现异常,请联系管理员");
        return commonResp;
    }
    /**
     * RuntimeException异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(RuntimeException e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getMessage());
        return commonResp;
    }
}

通过RuntimeException的handler捕获异常,通过e.getMessage()来获取异常文本信息,并在最上层的接口抛出。

但是以上并不是最优解,以为异常太多了,我们最好自定义一个异常类,并且通过枚举来管理异常的细分种类

首先在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/exception/BusinessExceptionEnum.java中

java 复制代码
package com.wz.nls.business.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;


@AllArgsConstructor
public enum BusinessExceptionEnum {
    DEMO_MOBILE_NOT_NULL("手机号不能为空!");

    @Getter
    private String desc;
}

然后在自定义类中使用它

java 复制代码
package com.wz.nls.business.exception;

import lombok.AllArgsConstructor;
import lombok.Data;


@Data
@AllArgsConstructor
public class BusinessException extends RuntimeException {
    private BusinessExceptionEnum e;
}

在拦截器/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java中这样定义

java 复制代码
    /**
     * BusinessException异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(BusinessException e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getE().getDesc());
        return commonResp;
    }

使用时,我们直接抛出枚举类的desc

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
//        if (mobile != null) {
//            criteria.andMobileNameEqualTo(mobile);
//        }
        if (StringUtils.isBlank(mobile)) {
            throw new BusinessException(BusinessExceptionEnum.DEMO_MOBILE_NOT_NULL);
        }
        criteria.andMobileNameEqualTo(mobile);
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }

痛点5

后台运维,看堆栈报错太复杂,如何简约化?

痛点5解决方案

/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/exception/BusinessException.java

重写fillInStackTrace方法防止递归,并且重写构造函数,原本使用父类可以传值进去,然后抛出异常的,

java 复制代码
throw new RuntimeException("手机不能为空");

现在在构造函数使用super,然后狸猫换太子~

java 复制代码
package com.wz.nls.business.exception;

import lombok.Data;


@Data
public class BusinessException extends RuntimeException {
    private BusinessExceptionEnum e;
    
    public BusinessException(BusinessExceptionEnum e) {
        super(e.getDesc());
        this.e = e;
    }
    
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}

此时不显示null了,而是正常的错误信息文本

相关推荐
Knight_AL2 小时前
Java 多态详解:概念、实现机制与实践应用
java·开发语言
C雨后彩虹2 小时前
volatile 实战应用篇 —— 典型场景
java·多线程·并发·volatile
xie_pin_an2 小时前
从二叉搜索树到哈希表:四种常用数据结构的原理与实现
java·数据结构
没有bug.的程序员2 小时前
Java 并发容器深度剖析:ConcurrentHashMap 源码解析与性能优化
java·开发语言·性能优化·并发·源码解析·并发容器
羊小猪~~3 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
kk哥88993 小时前
分享一些学习JavaSE的经验和技巧
java·开发语言
栈与堆3 小时前
LeetCode 21 - 合并两个有序链表
java·数据结构·python·算法·leetcode·链表·rust
lagrahhn3 小时前
Java的RoundingMode舍入模式
java·开发语言·金融
张彦峰ZYF3 小时前
商品供给域的工程化简要设计考量
后端·系统架构·商品模型·商品供给