一:编写统一包装类

package com.llll.demo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Resp implements Serializable {

    private String ret;
    private String msg;
    private Object info;

}

我这里用了lombok处理Getter、Setter、有参构造,无参构造,有不懂的同学可以自己去搜索一下。ret存放返回值(是否成功),msg存放提示信息(成功返回成功,失败返回失败原因),info存放接口返回信息。

二:自定义注解

package com.llll.demo.controller;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WrapResp {
}

    @Target可以简单理解为注解的使用范围

  • @Target(ElementType.TYPE) //接口、类、枚举、注解
  • @Target(ElementType.FIELD) //字段、枚举的常量
  • @Target(ElementType.METHOD) //方法
  • @Target(ElementType.PARAMETER) //方法参数
  • @Target(ElementType.CONSTRUCTOR) //构造函数
  • @Target(ElementType.LOCAL_VARIABLE)//局部变量
  • @Target(ElementType.ANNOTATION_TYPE)//注解
  • @Target(ElementType.PACKAGE) ///包

    @Retention可以用来修饰注解,是注解的注解,称为元注解。
    Retention注解有一个属性value——RetentionPolicy,RetentionPolicy有3个值:CLASS RUNTIME SOURCE
    按生命周期来划分可分为3类:

  1. RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  2. RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
  3. RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
    这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。

    @Documented注解
    Documented注解表明这个注解是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注解了文档化,它的注解成为公共API的一部分。

三:编写处理机制

package com.llll.demo.controller;

import com.llll.demo.model.Resp;
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;

@ControllerAdvice(basePackages = {"com.llll.demo.controller"})
public class WrapRespAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        boolean wrap = methodParameter.hasMethodAnnotation(WrapResp.class);
        if (!wrap) {
            WrapResp anno = methodParameter.getMethod() == null ? null : methodParameter.getMethod().getDeclaringClass().getAnnotation(WrapResp.class);
            if (anno != null) {
                wrap = true;
            }
        }
        return wrap;
    }

    @Override
    public Object beforeBodyWrite(Object resp, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        return Resp.builder().ret("0").msg("success").info(resp).build();
    }
}
  • @ControllerAdvice 设置拦截目录,basePackages 填写到你controller对应的包
  • ResponseBodyAdvice 统一响应处理

四:使用

    编写controller,在类上或者方法上添加@WrapResp注解。在类上添加,则针对类中所有方法全部包装;在方法上添加,则只针对此方法进行包装。

package com.llll.demo.controller;

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

@WrapResp
@RestController
@RequestMapping("/llll/demo")
public class StrategiesInstanceDataController {
	@GetMapping("/{id}/data")
    public Data getById(@PathVariable Long id) {
        return service.getById(id);
    }
}

返回结果

{
    "ret": "0",
    "msg": "success",
    "info": {
        "id": 7,
        "name": "柳柳柳柳丶",
        "version": 2
    }
}

五:统一异常处理

    springboot有多种方式进行异常处理,我这里是编写了父类controller,所有的controller继承父类。java是单继承多实现,所有尽量少用继承,目前项目controller的扩展性要求不是很高,所以使用继承实现问题不大。
    BaseController代码如下:

package com.llll.demo.controller;

import com.llll.demo.model.Resp;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * 处理异常的类,需要处理异常的Controller直接继承这个类
 */
public class BaseController {

    /**
     * 处理Controller抛出的异常
     *
     * @param e 异常实例
     * @return Controller层的返回值
     */
    @ExceptionHandler
    @ResponseBody
    public Object expHandler(Exception e) {
        e.printStackTrace();
        return Resp.builder().ret("1").msg("fail").info(e.getMessage()).build();
    }
}

    ExceptionHandler见名知意,异常处理器,此时所有controller继承BaseController即可;我们自己编写异常,演示异常之后的返回数据:

package com.llll.demo.controller;

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

@WrapResp
@RestController
@RequestMapping("/llll/demo")
public class StrategiesInstanceDataController extends BaseController{
	@GetMapping("/{id}/data")
    public Data getById(@PathVariable Long id) {
    	int i = 1 / 0;
        return service.getById(id);
    }
}

返回结果如下:

{
    "ret": "1",
    "msg": "fail",
    "info": "/ by zero"
}