前言

在之前的文章中我们有介绍过,如何更好、更简单的写好一个接口《接口返回值》,今天的这篇文章我们主要介绍,怎么统一处理下接口的返回格式问题。

问题分析

我们先来分析下我们所面临的问题在哪里,然后接着给出解决方案。在写一个接口时,我们通常会先统一定义一下接口的返回格式是什么,然后在跟前端去对接,通常的返回格式大体两种(我们以保存用户为例):

1.成功/失败响应格式不一致(此种方式作为我们默认的接口响应方式)

保存用户成功,响应体:

{
    "id": 10000,
    "pwd": "123123",
    "nickname": "nickname1",
    "img": "http://xxx.com/1.png",
    "status": "NORMAL",
    "createTime": 1517762718278
}

失败响应体(下面的格式是 spring boot 默认的错误响应格式,只不过我们在其基础上增加了一个 code 字段用于解释更详细的错误码)

{
    "status": 400,
    "error": "Bad Request",
    "message": "参数无效",
    "code": 10001,
    "path": "/user",
    "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
    "errors": [
        {
            "fieldName": "status",
            "message": "值是无效的"
        }
    ],
    "timestamp": 1515076067369
}

2.成功/失败响应体格式一致

保存用户成功,响应体:

{
    "code": 1,
    "msg": "成功",
    "data": {
        "id": 10000,
        "pwd": "123123",
        "nickname": "nickname1",
        "img": "http://xxx.com/1.png",
        "status": "NORMAL",
        "createTime": 1515076287882
    }
}

失败响应体:

{
    "code": 10001,
    "msg": "参数无效",
    "data": [
        {
            "fieldName": "status",
            "message": "值是无效的"
        }
    ]
}

那么如果我们想要的响应体格式是第二种,我们该如何写我们的代码呢?你可能想是这样么?

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PlatformResult addUser(@Validated @RequestBody User user) {
        user.setId("10000");
        user.setCreateTime(new Date());
        return PlatformResult.success(user);
    }

}

PlatformResult.success() 这段逻辑显然很多余,每个方法都要这样写一遍,所以上述方式并不是我们想要的,我们要的是

@ResponseResult(PlatformResult.class)
@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User addUser(@Validated @RequestBody User user) {
        user.setId("10000");
        user.setCreateTime(new Date());
        return user;
    }

}

我们加了一个自定义的注解 @ResponseResult(PlatformResult.class),参数 PlatformResult.class 告诉这个 Controller 类下的所有方法都以这个类 PlatformResult 的格式进行返回,这个注解可以标记在类或方法上,好了,我们的目的明朗了许多,要做的就是标记这个注解让它实现接口返回值格式控制这个功能,下面我们给出具体的实现方式。

实现思路

首先介绍下完成我们这次主要功能的几个类:

Result 是返回格式类的父接口(所有返回格式类都需要继承它)

PlatformResult 通用返回结果格式(我们上面说的第二种返回结果)

DefaultErrorResult 全局错误返回结果(我们上面说的第一种错误时的返回结果)

GlobalExceptionHandler 全局异常处理

ResponseResult 注解类(用于在Controller上指定返回值格式类)

ResponseResultInterceptor 拦截器(主要用于将 ResponseResult 注解类的标记信息传入 ResponseResultHandler 中)

ResponseResultHandler 响应体格式处理器(主要转换逻辑都在这里)

代码实现

下面将有一大片代码袭来,要顶住!哈哈~

Result 接口类

package cn.notemi.demo.result;

import java.io.Serializable;

/**
 * @desc 响应格式父接口
 */
public interface Result extends Serializable {
}

说明

理论上所有的返回格式类都需要实现该接口才能被使用

PlatformResult 通用返回结果

package cn.notemi.demo.result;

import cn.notemi.demo.enums.ResultCode;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @desc 平台通用返回结果
 */
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class PlatformResult implements Result {

    private static final long serialVersionUID = 874200365941306385L;

    private Integer code;

    private String msg;

    private Object data;

    public static PlatformResult success() {
        PlatformResult result = new PlatformResult();
        result.setResultCode(ResultCode.SUCCESS);
        return result;
    }

    public static PlatformResult success(Object data) {
        PlatformResult result = new PlatformResult();
        result.setResultCode(ResultCode.SUCCESS);
        result.setData(data);
        return result;
    }

    public static PlatformResult failure(ResultCode resultCode) {
        PlatformResult result = new PlatformResult();
        result.setResultCode(resultCode);
        return result;
    }

    public static PlatformResult failure(ResultCode resultCode, Object data) {
        PlatformResult result = new PlatformResult();
        result.setResultCode(resultCode);
        result.setData(data);
        return result;
    }

    public static PlatformResult failure(String message) {
        PlatformResult result = new PlatformResult();
        result.setCode(ResultCode.PARAM_IS_INVALID.code());
        result.setMsg(message);
        return result;
    }

    private void setResultCode(ResultCode code) {
        this.code = code.code();
        this.msg = code.message();
    }

}

DefaultErrorResult 默认全局错误返回格式

package cn.notemi.demo.result;

import cn.notemi.demo.enums.BusinessExceptionEnum;
import cn.notemi.demo.enums.ResultCode;
import cn.notemi.demo.exceptions.BusinessException;
import cn.notemi.demo.util.RequestContextUtil;
import cn.notemi.demo.util.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

import java.util.Date;

/**
 * @desc 默认全局错误返回结果
 *       备注:该返回信息是spring boot的默认异常时返回结果,目前也是我们服务的默认返回结果
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class DefaultErrorResult implements Result {

    private static final long serialVersionUID = 1899083570489722793L;

    /**
     * HTTP响应状态码 {@link HttpStatus}
     */
    private Integer status;

    /**
     * HTTP响应状态码的英文提示
     */
    private String error;

    /**
     * 异常堆栈的精简信息
     * 
     */
    private String message;

    /**
     * 我们系统内部自定义的返回值编码,{@link ResultCode} 它是对错误更加详细的编码
     * 
     * 备注:spring boot默认返回异常时,该字段为null
     */
    private Integer code;

    /**
     * 调用接口路径
     */
    private String path;

    /**
     * 异常的名字
     */
    private String exception;

    /**
     * 异常的错误传递的数据
     */
    private Object errors;

    /**
     * 时间戳
     */
    private Date timestamp;

    public static DefaultErrorResult failure(ResultCode resultCode, Throwable e, HttpStatus httpStatus, Object errors) {
        DefaultErrorResult result = DefaultErrorResult.failure(resultCode, e, httpStatus);
        result.setErrors(errors);
        return result;
    }

    public static DefaultErrorResult failure(ResultCode resultCode, Throwable e, HttpStatus httpStatus) {
        DefaultErrorResult result = new DefaultErrorResult();
        result.setCode(resultCode.code());
        result.setMessage(resultCode.message());
        result.setStatus(httpStatus.value());
        result.setError(httpStatus.getReasonPhrase());
        result.setException(e.getClass().getName());
        String path = RequestContextUtil.getRequest().getRequestURI();
        result.setPath(path);
        result.setTimestamp(new Date());
        return result;
    }

    public static DefaultErrorResult failure(BusinessException e) {
        BusinessExceptionEnum ee = BusinessExceptionEnum.getByEClass(e.getClass());
        if (ee != null) {
            return DefaultErrorResult.failure(ee.getResultCode(), e, ee.getHttpStatus(), e.getData());
        }

        DefaultErrorResult defaultErrorResult = DefaultErrorResult.failure(e.getResultCode() == null ? ResultCode.SUCCESS : e.getResultCode(), e, HttpStatus.OK, e.getData());
        if (StringUtil.isNotEmpty(e.getMessage())) {
            defaultErrorResult.setMessage(e.getMessage());
        }
        return defaultErrorResult;
    }

}

GlobalExceptionHandler 全局错误异常处理器

package cn.notemi.demo.handler;

import cn.notemi.demo.exceptions.BusinessException;
import cn.notemi.demo.result.DefaultErrorResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

/**
 * @desc 统一异常处理器
 */
@RestController
@ControllerAdvice
public class GlobalExceptionHandler extends BaseGlobalExceptionHandler {

    /** 处理400类异常 */
    @Override
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public DefaultErrorResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        return super.handleConstraintViolationException(e, request);
    }

    @Override
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public DefaultErrorResult handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) {
        return super.handleConstraintViolationException(e, request);
    }

    @Override
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public DefaultErrorResult handleBindException(BindException e, HttpServletRequest request) {
        return super.handleBindException(e, request);
    }

    @Override
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public DefaultErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        return super.handleMethodArgumentNotValidException(e, request);
    }

    /** 处理自定义异常 */
    @Override
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<DefaultErrorResult> handleBusinessException(BusinessException e, HttpServletRequest request) {
        return super.handleBusinessException(e, request);
    }

    /** 处理运行时异常 */
    @Override
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public DefaultErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
        //TODO 可通过邮件、微信公众号等方式发送信息至开发人员、记录存档等操作
        return super.handleThrowable(e, request);
    }

}

BaseGlobalExceptionHandler 全局异常处理基础类

package cn.notemi.demo.handler;

import cn.notemi.demo.enums.ResultCode;
import cn.notemi.demo.exceptions.BusinessException;
import cn.notemi.demo.helper.ParameterInvalidItemHelper;
import cn.notemi.demo.model.bo.ParameterInvalidItem;
import cn.notemi.demo.result.DefaultErrorResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.List;

/**
 * @desc 全局异常处理基础类
 */
@Slf4j
public abstract class BaseGlobalExceptionHandler {

    /**
     * 违反约束异常
     */
    protected DefaultErrorResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        log.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e);
        List<ParameterInvalidItem> parameterInvalidItemList = ParameterInvalidItemHelper.convertCVSetToParameterInvalidItemList(e.getConstraintViolations());
        return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList);
    }

    /**
     * 处理验证参数封装错误时异常
     */
    protected DefaultErrorResult handleConstraintViolationException(HttpMessageNotReadableException e, HttpServletRequest request) {
        log.info("handleConstraintViolationException start, uri:{}, caused by: ", request.getRequestURI(), e);
        return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST);
    }

    /**
     * 处理参数绑定时异常(反400错误码)
     */
    protected DefaultErrorResult handleBindException(BindException e, HttpServletRequest request) {
        log.info("handleBindException start, uri:{}, caused by: ", request.getRequestURI(), e);
        List<ParameterInvalidItem> parameterInvalidItemList = ParameterInvalidItemHelper.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult());
        return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList);
    }

    /**
     * 处理使用@Validated注解时,参数验证错误异常(反400错误码)
     */
    protected DefaultErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        log.info("handleMethodArgumentNotValidException start, uri:{}, caused by: ", request.getRequestURI(), e);
        List<ParameterInvalidItem> parameterInvalidItemList = ParameterInvalidItemHelper.convertBindingResultToMapParameterInvalidItemList(e.getBindingResult());
        return DefaultErrorResult.failure(ResultCode.PARAM_IS_INVALID, e, HttpStatus.BAD_REQUEST, parameterInvalidItemList);
    }

    /**
     * 处理通用自定义业务异常
     */
    protected ResponseEntity<DefaultErrorResult> handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.info("handleBusinessException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());

        DefaultErrorResult defaultErrorResult = DefaultErrorResult.failure(e);
        return ResponseEntity
                .status(HttpStatus.valueOf(defaultErrorResult.getStatus()))
                .body(defaultErrorResult);
    }

    /**
     * 处理未预测到的其他错误(反500错误码)
     */
    protected DefaultErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
        log.error("handleThrowable start, uri:{}, caused by: ", request.getRequestURI(), e);
        return DefaultErrorResult.failure(ResultCode.SYSTEM_INNER_ERROR, e, HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

说明

上面用到了一些自定义异常基类(BusinessException),对于这个类它会有很多子类去继承它,例如:
参数异常、数据已存在异常、无权限异常等等,这些类大家可以根据自己的业务扩展

ResponseResult 注解类

package cn.notemi.demo.annotations;

import cn.notemi.demo.result.PlatformResult;
import cn.notemi.demo.result.Result;

import java.lang.annotation.*;

/**
 * @desc 接口返回结果增强  会通过拦截器拦截后放入标记,在WebResponseBodyHandler进行结果处理
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseResult {

    Class<? extends Result> value() default PlatformResult.class;

}

说明

这里我们默认 PlatformResult 使用这个类作为返回格式,所以@ResponseResult 等价于@ResponseResult(PlatformResult.class)

ResponseResultInterceptor 拦截器

package cn.notemi.demo.intercepter;

import cn.notemi.demo.annotations.ResponseResult;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @desc 接口响应体控制拦截器
 */
public class ResponseResultInterceptor implements HandlerInterceptor {

    public static final String RESPONSE_RESULT = "RESPONSE-RESULT";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            final HandlerMethod handlerMethod = (HandlerMethod) handler;
            final Class<?> clazz = handlerMethod.getBeanType();
            final Method method = handlerMethod.getMethod();
            if (clazz.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(RESPONSE_RESULT, clazz.getAnnotation(ResponseResult.class));
            } else if (method.isAnnotationPresent(ResponseResult.class)) {
                request.setAttribute(RESPONSE_RESULT, method.getAnnotation(ResponseResult.class));
            }
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // nothing to do
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // nothing to do
    }

}

开启拦截器配置

package cn.notemi.demo.config;

import cn.notemi.demo.intercepter.HeaderParamsCheckInterceptor;
import cn.notemi.demo.intercepter.LoginAuthInterceptor;
import cn.notemi.demo.intercepter.ResponseResultInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {

    @Bean
    public ResponseResultInterceptor responseResultInterceptor() {
        return new ResponseResultInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //响应结果控制拦截
        registry.addInterceptor(responseResultInterceptor()).addPathPatterns("/**");
    }

}

ResponseResultHandler 响应体格式处理器

package cn.notemi.demo.handler;

import cn.notemi.demo.annotations.ResponseResult;
import cn.notemi.demo.constant.HeaderConstants;
import cn.notemi.demo.enums.ApiStyleEnum;
import cn.notemi.demo.intercepter.ResponseResultInterceptor;
import cn.notemi.demo.result.DefaultErrorResult;
import cn.notemi.demo.result.PlatformResult;
import cn.notemi.demo.result.Result;
import cn.notemi.demo.util.JsonUtil;
import cn.notemi.demo.util.RequestContextUtil;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @desc 接口响应体处理器
 */
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        HttpServletRequest request = RequestContextUtil.getRequest();
        ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(ResponseResultInterceptor.RESPONSE_RESULT);
        return responseResultAnn != null && !ApiStyleEnum.NONE.name().equalsIgnoreCase(request.getHeader(HeaderConstants.API_STYLE));
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ResponseResult responseResultAnn = (ResponseResult) RequestContextUtil.getRequest().getAttribute(ResponseResultInterceptor.RESPONSE_RESULT);

        Class<? extends Result> resultClazz = responseResultAnn.value();

        if (resultClazz.isAssignableFrom(PlatformResult.class)) {
            if (body instanceof DefaultErrorResult) {
                DefaultErrorResult defaultErrorResult = (DefaultErrorResult) body;
                return PlatformResult.builder()
                        .code(defaultErrorResult.getCode())
                        .msg(defaultErrorResult.getMessage())
                        .data(defaultErrorResult.getErrors())
                        .build();
            } else if (body instanceof String) {
                return JsonUtil.object2Json(PlatformResult.success(body));
            }

            return PlatformResult.success(body);
        }

        return body;
    }

}

说明

上述代码用到了一个工具类(RequestContextHolderUtil)用于获取request对象,如果感兴趣可以看下这篇文章 工具类分享之《RequestContextHolderUtil》

@ControllerAdvice、ResponseBodyAdvice 这两个类是本功能的关键使用类,用于接口的响应体增强,其中 supports 方法用于判断是否需要做增强转化,beforeBodyWrite 方法用于增加逻辑实现

supports 方法中,加了个小功能,当调用人员不想要封装结果时,可以在 header 上设置参数 Api-Style=none

测试

package cn.notemi.demo.controller;

import cn.notemi.demo.annotations.LoginAuth;
import cn.notemi.demo.annotations.ResponseResult;
import cn.notemi.demo.model.bo.LoginUser;
import cn.notemi.demo.model.po.User;
import cn.notemi.demo.model.qo.LoginQO;
import cn.notemi.demo.model.vo.LoginVO;
import cn.notemi.demo.repository.UserRepository;
import cn.notemi.demo.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Title:UserController
 **/
@RestController
@RequestMapping("/api/user")
@ResponseResult
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/all")
    public List<User> users() {
        return userRepository.findAll();
    }
}

结束语

接口响应体统一格式转化,这个功能就完成了,这样的封装可以少些很多重复且无用的代码,何乐而不为呢?

GitHub地址:https://github.com/FlickerMi/notemi-demo

非特殊说明,本博所有文章均为博主原创。

如若转载,请注明出处:http://www.notemi.cn/springboot-practice---unified-encapsulation-of-interface-response-body.html

添加新评论

icon_question.pngicon_razz.pngicon_sad.pngicon_evil.pngicon_exclaim.pngicon_smile.pngicon_redface.pngicon_biggrin.pngicon_surprised.pngicon_eek.pngicon_confused.pngicon_cool.pngicon_lol.pngicon_mad.pngicon_twisted.pngicon_rolleyes.pngicon_wink.pngicon_idea.pngicon_arrow.pngicon_neutral.pngicon_cry.pngicon_mrgreen.png

16 + 15 =


已有 14 条评论
    Scott he Scott he
    Apr 25, 2024 回复

    有个问题,结合 swagger 的话,好像不能生成实际的接口信息,因为最终信封体的封装是在 responsebodyadvice 里面,springdoc 无法知道


    青山 青山
    Sep 1, 2023 回复

    很认真看完了博主的文章,GitHub代码也看了,很不错,文章写的也很认真,但是提个小小的建议哈,封装太过严重了,统一状态码,统一响应格式,统一异常不用这么复杂的,而且有很多东西在spring boot中就已经实现了,直接拿来用就行的

      Sep 12, 2023 回复

      谢谢建议,现在确实有更优雅的做法了。

        Scott he Scott he
        Apr 25, 2024 回复

        请问现在有什么更优雅的方式?




    grow grow
    Dec 23, 2020 回复

    学习了,很详细,谢谢. 受益匪浅


    新手村程序员 新手村程序员
    Nov 2, 2020 回复

    有没有数据库,发一份呀 icon_biggrin.png icon_biggrin.png icon_biggrin.png icon_biggrin.png icon_biggrin.png icon_biggrin.png icon_biggrin.png


    柒月君 柒月君
    Jun 2, 2020 回复

    看完受益匪浅,我现在自己写东西就想遵守restful和写出封装比较多的 简短代码。这次可以继续改造我得代码了。

    我就是所有都返回一样的东西。写的有点累

      Jun 5, 2020 回复

      有重复的代码,能抽出来就抽吧。



    Jun 2, 2020 回复

    刚刚看到@Builder 我从来没用过,刚刚百度了下,
    发现别人介绍 @Builder 是会生成全参数的构造方法的。
    所以@AllArgsConstructor是不是就不用写了?
    Lombok 我只用get,set,data,顶多有个链式结构,类似builder吧

      Jun 5, 2020 回复

      @AllArgsConstructor和@Builder还是有点不一样的,前者是生成全参数的构造方法,后者其实就是实现的建造者模式,也可以很方便的构建对象,二者根据情况使用吧。

        Jun 6, 2020 回复

        不加AllArgsConstructor 编译不通过,
        我搜的文章是说@Builder实现建造者模式并且声称全参数的构造方法。诶

          Jun 6, 2020 回复

          我刚刚看了编译后的class,@Builder确实会生成全参数的构造函数,不过该构造函数访问修饰符为default(friendly),只能当前包能访问。





    Jun 2, 2020 回复

    第六个代码块
    user.setId("10000);缺个分号 icon_lol.png

      Jun 5, 2020 回复

      谢谢指正。