-》做javaweb的在开发数据接口的时候都会封装一套统一的结果类,把需要返回给前端的数据放到结果类中的数据域,然后在返回给前端。返回的数据结构倒是规范了,但是每次都需要手动的set数据,是不是很麻烦。
如标题所示写一个能把数据自动封装到结果类的小功能:
1、首先想到的是aop来实现,自定义一个注解来标识需要自动封装返回值的handler,现实会是这么如意的吗?我们接着往下看。
a)简约的结果类
@Data
@AllArgsConstructor
public class AjaxResult {
private Integer code;
private String message;
private Object data;
public static AjaxResult success(Object data) {
return new AjaxResult(HttpStatus.OK.value(), null, data);
}
public static AjaxResult fail(String msg) {
return new AjaxResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, null);
}
}
b)自定义注解
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoResult {
}
c)aop环绕通知
@Component
@Aspect
@Slf4j
public class AutoResultAop {
@Pointcut("@annotation(cn.fzy.annotation.AutoResult)")
public void point() {
}
@Around(value = "point()")
public Object around(ProceedingJoinPoint pjd) {
Object res = null;
try {
res = pjd.proceed();
log.info(res.toString());
} catch (Throwable e) {
e.printStackTrace();
}
// 包裹成结果类返回
return AjaxResult.success(res);
}
}
d)测试的数据接口
@RestController
public class TestController {
@GetMapping("/hi")
@AutoResult
public String sayHi() {
return "hi";
}
}
调用该接口测试,报一个强制转换的错,想想也是情理之中的事。
java.lang.ClassCastException: cn.fzy.vo.AjaxResult cannot be cast to java.lang.String
尽然这种方案不得行,那只有另辟蹊径了。思考了一下:controller上加了@ResponseBody会把数据转换为json格式返回给前台,那我们能不能在这里寻找突破口。
尽然有了方向在网上查询资料得知,controller的返回值都将由HandlerMethodReturnValueHandler来处理。
#supportsReturnType(MethodParameter returnType)
:返回false将会由下一个HandlerMethodReturnValueHandler来处理,当返回true是会执行下一面方法?。
#handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
:returnValue即为controller返回的值,对数据的处理逻辑在这个方法实现。
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
它的实现类有很多,那么哪一个是返回json格式的呢?最终定位到:RequestResponseBodyMethodProcessor。看看它是怎么重写这两个方法的。
#supportsReturnType做了什么:方法上有@ResponseBody或者类上有@ResponseBody返回true.
#handleReturnValue做了什么:mavContainer.setRequestHandled(true)告诉后面的HandlerMethodReturnValueHandler我已经接手了,不需要后面的HandlerMethodReturnValueHandler了。writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage)把数据的json格式写入response里面返回给前台,这里就不贴源码了,下去你们可以点下去看看。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
怎么实现都知道了,那我们来愉快的添加一个自己的HandlerMethodReturnValueHandler吧
a)自定义个returnHandler实现HandlerMethodReturnHandler接口,并重写方法
public class AutoResultReturnHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter methodParameter) {
return methodParameter.getMethodAnnotation(AutoResult.class) != null
|| methodParameter.getDeclaringClass().getAnnotation(AutoResult.class) != null;
}
@Override
public void handleReturnValue(Object o, MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) {
modelAndViewContainer.setRequestHandled(true);
HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
response.setContentType("text/json;charset=UTF-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.print(JSON.toJSONString(AjaxResult.success(o)));
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
}
}
}
}
b)注册自定义的returnHandler
@Configuration
@EnableWebMvc
public class WebMvcConfig implements InitializingBean {
@Autowired
RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Override
public void afterPropertiesSet() {
List returnValueHandlers = requestMappingHandlerAdapter
.getReturnValueHandlers();
List list = new ArrayList<>();
list.add(new AutoResultReturnHandler());//自定义returnHandler
list.addAll(returnValueHandlers);
requestMappingHandlerAdapter.setReturnValueHandlers(list);
}
}
遛一下试试:
@RestController
public class TestController {
@GetMapping("/hi")
@AutoResult
public String sayHi() {
return "hi";
}
@GetMapping("/bye")
public String sayBye() {
return "bye";
}
}
加上了@AutoResult注解的,调用该接口测试:
没加注解的
最后:如果执行有异常抛出可以在全局异常处理里面封装相同的结果类。
举一反三:
需要对返回数据做加密操作的同理(如果不改变数据类型,第一种aop也能实现)
就这么愉快的结束了!希望对你有所微薄的帮助。
向上的路并不拥挤,到多数人选择了安逸!--it疯子也