java实现自定义包装注解的方式不少,可以采用aop方式,还可以单纯采用拦截器配合ResponseBodyAdvice(响应结果处理)方式。这篇文章我用到的是第二种。
1. 自定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ResponseResult {
}
作用:自定义的包装注解,可在controller类/具体接口方法上添加,最后实现效果就是检测到有这个注解标记的话,可对范围内的接口返回值进行统一格式包装。
2. 定义一个拦截器处理@ResponseResult注解
@Component
@Slf4j
public class ResponseResultInterceptor implements HandlerInterceptor {
/**
1. 标记名称
*/
public static final String RESPONSE_RESULT_ANN="RESPONSE-RESULT-ANN";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//请求的方法
if(handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod= (HandlerMethod) handler;
final Class<?> clazz=handlerMethod.getBeanType();
final Method method=handlerMethod.getMethod();
log.info("拦截该请求接口,判断方法或该方法所在的类上是否有统一包装的注解@ResponseResult:{}->{}",clazz.getSimpleName(),method.getName());
//判断是否在类对象上面加了注解
if (clazz.isAnnotationPresent(ResponseResult.class)){
//设置此请求返回体,需要包装,往下传递,在ResponseAdvice接口进行判断
request.setAttribute(RESPONSE_RESULT_ANN,clazz.getAnnotation(ResponseResult.class));
//方法体上是否有注解
}else if (method.isAnnotationPresent(ResponseResult.class)){
//设置此请求返回体,需要包装,往下传递,在ResponseAdvice接口进行判断
request.setAttribute(RESPONSE_RESULT_ANN,method.getAnnotation(ResponseResult.class));
}
}
return true;
}
}
注:HandlerInterceptor接口是必须要实现的,是spring web中的拦截器接口。而提到的ResponseAdvice接口是在下一步完成。
3. 注册自定义的拦截器
拦截器要想生效需要被注册。
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Autowired
private ResponseResultInterceptor responseResultInterceptor;
/**
1. 注册拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 针对@ResponseResult注解的拦截器
registry.addInterceptor(responseResultInterceptor).addPathPatterns("/**");
// - /**: 匹配所有路径
// - /com/**:匹配 /com/ 下的所有路径
// - /com/*:只匹配 /com/luopan,不匹配 /com/luopan/guides ("/*"只匹配一级子目录,"/**"匹配所有子目录)
}
}
responseResultInterceptor就是我们上一步自定义的拦截器。
4.实现对请求响应后的结果的处理
@Slf4j
@RestControllerAdvice(basePackages = "com.luopan.guides.web.controller")
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
/**
* 标记名称
*/
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";
@Autowired
private ObjectMapper objectMapper;
/**
* 请求是否包含了 包装注解 标记,没有就直接返回,不需要重写返回体
*/
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
assert sra != null;
HttpServletRequest request = sra.getRequest();
//判断请求 是否有包装标记
ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN);
return responseResultAnn != null;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof String) {
return objectMapper.writeValueAsString(Result.ok(body));
}
log.info("有包装注解,拦截返回接口数据成功,封装成统一返回格式");
return Result.ok(body);
}
}
注:
1.@RestControllerAdvice是需要处理的范围,一般指定controller层
2.自定义的包装注解是否存在的判断是通过定义的标记名称来实现
3.这个类要想生效,需要在controller层中调用方法上加入@RequstMapping和@ResponseBody
4.文中的Result.ok()是我自定义的返回格式,可根据需求自行定义。
5.加上
if (body instanceof String) {
return objectMapper.writeValueAsString(Result.ok(body));
}
的目的是因为可能对返回类型是String的话可能无法识别解析。
最终效果:
只要加了该注解,就会对接口的返回类型进行统一包装,不用重复劳动,写上类似return Result.ok(JsonUtil.json2Map(response))
;
这种代码。
后续也可在此基础上实现同一异常处理。
ps:因为目前所在公司属于个人任务时间充裕那种,所以我不用像之前那样已赶紧做出来为主,更多的思考,优化,造轮子才是有成就感。希望看到的朋友一起共进步,讨论。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/92309.html