0、需求
- 统一封装返回结果,包括code、message、data数据
- 不用手动封装,通过自定义注解标记即实现封装
- 如果controller结果已经手动封装,则不重复封装
1、项目结构
2、创建自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseBoxing {
}
3、创建封装异常:ResponseBoxingException
import lombok.Getter;
public class ResponseBoxingException extends RuntimeException {
@Getter
private Integer code;
@Getter
private final String message;
public ResponseBoxingException(String message) {
this.message = message;
}
public ResponseBoxingException(Integer code, String message) {
this.code = code;
this.message = message;
}
}
4、通用响应类:Response
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Data
public class Response {
public static final Integer SuccessCode = 200;
public static final Integer FailCode = 500;
private Integer code;
private String message;
private Object data;
@JsonIgnore
public Boolean isSuccess() {
return SuccessCode.equals(getCode());
}
public static Response success(Object data) {
Response response = new Response();
response.setCode(SuccessCode);
response.setMessage("success");
response.setData(data);
return response;
}
public static Response fail(String message) {
Response response = new Response();
response.setCode(FailCode);
response.setMessage(message);
return response;
}
public static Response fail(Integer code, String message) {
Response response = new Response();
response.setCode(code);
response.setMessage(message);
return response;
}
}
5、自动封装切面类:ResponseBoxingAspect
import com.example.demo.box.annotation.ResponseBoxing;
import com.example.demo.box.exception.ResponseBoxingException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class ResponseBoxingAspect {
@Around(value = "@within(com.example.demo.box.annotation.ResponseBoxing) || @annotation(com.example.demo.box.annotation.ResponseBoxing)")
public Object afterControllerApiInvoking(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
ResponseBoxing annotation = AnnotatedElementUtils.getMergedAnnotation(signature.getMethod(), ResponseBoxing.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.getMergedAnnotation(signature.getMethod().getDeclaringClass(), ResponseBoxing.class);
}
assert null != annotation;
Object result = null;
try {
result = point.proceed();
if (result == null) {
throw new ResponseBoxingException("got null from response");
}
if (result instanceof Response) {
return result;
}
result = Response.success(result);
return result;
} catch (ResponseBoxingException e) {
log.error(e.getMessage(), e);
if (e.getCode() != null) {
result = Response.fail(e.getCode(), e.getMessage());
} else {
result = Response.fail(e.getMessage());
}
return result;
} catch (Throwable throwable) {
throw new ResponseBoxingException(throwable.getMessage());
}
}
}
6、测试类:TestController
import com.example.demo.box.annotation.ResponseBoxing;
import com.example.demo.box.exception.ResponseBoxingException;
import com.example.demo.box.plugin.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@ResponseBoxing
@Slf4j
public class TestController {
@GetMapping("/test1/{id}")
public Object test1(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
result.put("result", "111:" + id);
return result;
}
@GetMapping("/test2/{id}")
public Object test2(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
result.put("result", "111:" + id);
throw new RuntimeException("exception");
}
@PostMapping("/test3")
public Object test3(@RequestBody Map<String, Object> map) {
return map;
}
@GetMapping("/test4/{id}")
public Object test4(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
result.put("result", "111:" + id);
throw new ResponseBoxingException("用户名错误");
}
@GetMapping("/test5/{id}")
public Object test5(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
result.put("code", "111:" + id);
return result;
}
@GetMapping("/test6/{id}")
public Object test6(@PathVariable Integer id) {
return Response.success("成功");
}
}
7、pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>responseBox</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>responseBox</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>