使⽤SpringAOP和⾃定义注解统⼀API返回值格式
摘要:统⼀接⼝返回值格式后,可以提⾼项⽬组前后端的产出⽐,降低沟通成本。因此,在借鉴前⼈处理⽅法的基础上,通过分析资料,探索建⽴了⼀套使⽤Spring AOP和⾃定义注解⽆侵⼊式地统⼀返回数据格式的⽅法。
§前⾔
  我们封装所有的Controller中接⼝返回结果,将其处理为统⼀返回数据结构后,可以提⾼前后端对接效率,降低沟通成本。⽽使⽤Spring AOP和⾃定义注解⽆侵⼊式地统⼀返回数据格式,则可以避免在每⼀个api上都处理返回格式,从⽽使后端开发节约开发时间,更加专注于开发业务逻辑。
  后端返回给前端的⾃定义返回格式如下:
{
"code":200,
"message":"OK",
"data":{
}
}
其中的三个参数的含义如下:
code: 返回状态码;
message: 返回信息的描述;
data: 返回值。
§直接修改API返回值类型
  Spring AOP和⾃定义注解的基本概念可以分别参考《》和《》。下⾯定义返回数据格式的封装类:
sult;
import lombok.Getter;
import lombok.ToString;
import java.io.Serializable;
@Getter
@ToString
public class ResultData<T> implements Serializable {
private static final long serialVersionUID = 4890803011331841425L;
/** 业务错误码 */
private Integer code;
/** 信息描述 */
private String message;
/** 返回参数 */
private T data;
private ResultData(ResultStatus resultStatus, T data) {
spring de = Code();
this.data = data;
}
/** 业务成功返回业务代码和描述信息 */
public static ResultData<Void> success() {
return new ResultData<Void>(ResultStatus.SUCCESS, null);
}
/** 业务成功返回业务代码,描述和返回的参数 */
public static <T> ResultData<T> success(T data) {
return new ResultData<T>(ResultStatus.SUCCESS, data);
}
/** 业务成功返回业务代码,描述和返回的参数 */
public static <T> ResultData<T> success(ResultStatus resultStatus, T data) {
if (resultStatus == null) {
return success(data);
}
return new ResultData<T>(resultStatus, data);
}
/** 业务异常返回业务代码和描述信息 */
public static <T> ResultData<T> failure() {
return new ResultData<T>(ResultStatus.INTERNAL_SERVER_ERROR, null);
/** 业务异常返回业务代码,描述和返回的参数 */
public static <T> ResultData<T> failure(ResultStatus resultStatus) {
return failure(resultStatus, null);
}
/** 业务异常返回业务代码,描述和返回的参数 */
public static <T> ResultData<T> failure(ResultStatus resultStatus, T data) {
if (resultStatus == null) {
return new ResultData<T>(ResultStatus.INTERNAL_SERVER_ERROR, null);
}
return new ResultData<T>(resultStatus, data);
}
}
=== 我是分割线 ===
sult;
import lombok.Getter;
import lombok.ToString;
import org.springframework.http.HttpStatus;
@ToString
@Getter
public enum ResultStatus {
SUCCESS(HttpStatus.OK.value(), "OK"),
BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "Bad Request"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error"),    TOO_MANY_REQUESTS(HttpStatus.TOO_MANY_REQUESTS.value(), "请勿重复请求"),
USER_NOT_FIND(-99, "请登陆");
/**
* 业务状态码
*/
private Integer code;
/**
* 业务信息描述
*/
private String message;
ResultStatus(Integer code, String message) {
}
}
  在UserController类中添加测试函数,其返回值直接使⽤上⾯定义的ResultData:
/**
* @author Wiener
*/
@RestController
@RequestMapping("/user")
public class UserController {
private static Logger logger = Logger(UserController.class);
/**
* ⽰例地址 xxx/user/getResultById?userId=1090330
*
* @author Wiener
*/
@GetMapping(value ="/getResultById", produces = "application/json; charset=utf-8")
public ResultData getResultById(Long userId) {
User user = new User();
user.setId(userId);
user.setAddress("测试地址是 " + UUID.randomUUID().toString());
logger.String());
return ResultData.success(user);
}
}
  启动项⽬,在浏览器中输⼊URL,得到如下结果:
{
"code":200,
"message":"OK",
"data":{
"id":1090330,
"userName":null,
"age":null,
"address":"测试地址是 f057181c-e7e2-41ec-9066-0e72619f0f86",
"mobilePhone":null
}
  说明已经成功定义返回数据格式,但是,这⾥并没有使⽤⾃定义注解和Spring AOP这两个知识点。客官莫急,待我喝完这杯咖啡,且听我娓娓道来。
§设置全局返回值类型
  下⾯⾸先定义⼀个⽤于设置全局切⼊点的⾃定义注解@ResponseResultBody。参考⽂献中强调,此⾃定义注解需要添加
@ResponseBody注解,这是画蛇添⾜,下⽂的测试⽤例将给⼤家演⽰不添加也可以达到同样的⽬的。
wiener.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ResponseResultBody {
}
  通过实现ResponseBodyAdvice接⼝,基于上述⾃定义注解@ResponseResultBody获取切⾯,封装返回值格式:
sult;
wiener.aop.ResponseResultBody;
import MethodParameter;
import annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.verter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.hod.annotation.ResponseBodyAdvice;
import java.lang.annotation.Annotation;
@RestControllerAdvice
public class ResultBodyAdvice implements ResponseBodyAdvice<Object> {
private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;
/**
* 基于⾃定义注解判断类或者⽅法是否使⽤了注解 @ResponseResultBody,使⽤就拦截
* @return boolean. true:执⾏函数beforeBodyWrite;否则,不执⾏
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return AnnotatedElementUtils.ContainingClass(), ANNOTATION_TYPE)
|| returnType.hasMethodAnnotation(ANNOTATION_TYPE);
}
/**
* 当类或者⽅法使⽤了 @ResponseResultBody 就会调⽤这个⽅法
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 避免重复封装响应报⽂
if (body instanceof ResultData) {
return body;
}
// 使⽤约定全局返回格式封装响应报⽂
return ResultData.success(body);
}
}
  修改测试⽤例,添加⾃定义注解@ResponseResultBody:
/**
* @author Wiener
*/
@RestController
@RequestMapping("/user")
@ResponseResultBody
public class UserController {
private static Logger logger = Logger(UserController.class);
* ⽰例地址 /user/getUserTestById?userId=1090330
*/
@GetMapping(value ="/getUserTestById", produces = "application/json; charset=utf-8")
public User getUserTestById(Long userId) throws Exception {
User user = new User();
user.setId(userId);
user.setAddress("测试地址是 " + UUID.randomUUID().toString());
logger.String());
return user;
}
/
**
* /user/getResult?userId=1090330
* @param user
* @return
*/
@PostMapping(value ="/getResult", produces = "application/json; charset=utf-8")
public ResultData getResult(@RequestBody User user) {
user.setAddress("测试地址是 " + UUID.randomUUID().toString());
logger.String());
return ResultData.success(user);
}
}
  启动服务,在Postman中请求getResult函数,可以得到如下结果:
在浏览器中请求API getUserTestById,可以得到如下结果:
说明成功统⼀返回值格式。
§⼩结
  本篇⽂章的内容基本上就是这些,我们来总结⼀下。在控制层添加⾃定义注解@ResponseResultBody后,我们就可以通过Spring AOP 定义基于此注解的切⾯。如果⼀个代理类使⽤了此注解,那么就封装其返回值;否则,不处理。
§Reference

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。