起因
关于使用AOP去实现统一返回接口在之前的博客中我们已经实现了,但我突然突发奇想,SpringBoot中异常类的统一返回好像是通过@RestControllerAdvice
这个注解去完成的,那我是否也可以通过这个注解去实现统一返回接口。
正文
这个方法主要是通过@ControllerAdvice
+ ResponseBodyAdvice
实现统一返回结果。其实本质来说和aop实现是相通的明白一个另一个就非常好理解了。
(Result 的代码我就不在这边重复贴了,读者可以去我直接用AOP实现的博客中拿)
自定义注解
import com.study.project.common.BaseResponse;
import com.study.project.common.ResultCode;
import java.lang.annotation.*;
import static com.study.project.common.ResultCode.SUCCESS;
/**
* @date 2023/2/18
*/
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FunctionResult {
String value() default "";
//默认code为成功
ResultCode code() default SUCCESS;
}
自定义一个响应拦截
import com.study.project.annotation.FunctionResult;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* @author Chengming.Zhang
* @date 2023/2/18
* ResponseBodyAdvice主要是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
*其实也就是采用了AOP的思想,对返回值进行一次修改。
*/
@RestControllerAdvice
public class FunctionResponseBodyAdvice implements ResponseBodyAdvice {
//判断当前类上是否有@FunctionResult
protected boolean isFunctionResult(MethodParameter returnType) {
/**
* getContainingClass() 获取当前类的信息
* isAnnotationPresent 判断当前类上是否存在某个注解
*/
Class<?> containingClass = returnType.getContainingClass();
boolean annotationPresent = containingClass.isAnnotationPresent(FunctionResult.class);
Annotation[] annotations = containingClass.getAnnotations();
return returnType.getContainingClass().isAnnotationPresent(FunctionResult.class);
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return isFunctionResult(returnType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Method method = returnType.getMethod();
Class<?> methodReturnType = method.getReturnType();
//判断是否为void的方法
if (methodReturnType.equals(void.class)) {
return body;
}
//判断当前方法是否有@FunctionResult注解,如果没有则全部按照成功返回,如果有则根据具体指定的返回码以及返回内容返回
FunctionResult result = returnType.getMethod().getAnnotation(FunctionResult.class);
if (result == null) {
return new BaseResponse(ResultCode.SUCCESS, body);
}
ResultCode code = result.code();
return new BaseResponse(result.code(), body);
}
}
controller类
import com.study.project.annotation.FunctionResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @date 2023/2/4
*/
@FunctionResult
@RestController
public class TestController {
@RequestMapping("/test5")
public int test5(){
return 1;
}
@RequestMapping("/test6")
public void test6(){
System.err.println("test6");
}
}
测试
你是不是以为代码已经结束?
其实这个代码是有问题hhhhh,当接口的返回类型是String的时候就会提示报错
看控制台的报错信息发现是接口的返回参数转换的时候报错了,于是我们根据控制台上的报错信息进行断点排查
首先我们找到控制台中的第一行的报错类StringHttpMessageConverter.java
中的addDefaultHeaders
方法,发这个方法其实是重新了他父类的AbstractHttpMessageConverter
的方法
在AbstractHttpMessageConverter
中的方法Result的参数是T,但是StringHttpMessageConverter
在重写的时候将其转为了String,因此当ResponseBodyAdvice
返回Result格式的时候就会报错,所以我们就需要在ResponseBodyAdvice
中需要单独处理一下String类型。
完整代码
import cn.hutool.json.JSONUtil;
import com.study.project.annotation.FunctionResult;
import jdk.nashorn.internal.objects.annotations.Function;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* @author Chengming.Zhang
* @date 2023/2/18
* ResponseBodyAdvice主要是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
*其实也就是采用了AOP的思想,对返回值进行一次修改。
*/
@RestControllerAdvice
public class FunctionResponseBodyAdvice implements ResponseBodyAdvice {
//判断当前类上是否有@FunctionResult
protected boolean isFunctionResult(MethodParameter returnType) {
/**
* getContainingClass() 获取当前类的信息
* isAnnotationPresent 判断当前类上是否存在某个注解
*/
Class<?> containingClass = returnType.getContainingClass();
boolean annotationPresent = containingClass.isAnnotationPresent(FunctionResult.class);
Annotation[] annotations = containingClass.getAnnotations();
return returnType.getContainingClass().isAnnotationPresent(FunctionResult.class);
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return isFunctionResult(returnType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Method method = returnType.getMethod();
Class<?> methodReturnType = method.getReturnType();
if (methodReturnType.equals(void.class)) {
return body;
}
//判断当前方法是否有@FunctionResult注解,如果没有则全部按照成功返回,如果有则根据具体指定的返回码以及返回内容返回
FunctionResult result = returnType.getMethod().getAnnotation(FunctionResult.class);
if (result == null) {
if (body instanceof String) {
return JSONUtil.toJsonStr(new BaseResponse(ResultCode.SUCCESS, body));
}
return new BaseResponse(ResultCode.SUCCESS, body);
}
if (body instanceof String) {
return JSONUtil.toJsonStr(new BaseResponse(result.code(), body));
}
return new BaseResponse(result.code(), body);
}
}