一:编写统一包装类
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类:
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
- 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"
}