springboot⼯程⾃定义response注解、⾃定义规范化返回数据
结构
1、需求背景
在做项⽬的时候你可能接到这样的需求,对于springboot项⽬⽽⾔,公司⼤佬们说默认的返回数据结构不能满⾜客户的需要,咱们⾃⼰必须封装出⽜逼的数据结构,看起来很吊很吊的那种,这样对外提供的接⼝⽂档才⽜逼,让别⼈看起来我司很正规,有⼀套⾃⼰的规范,巴拉巴拉巴拉⼀⼤堆。。。。
其实这个情况在各个公司还是⽐较常见的。但具体咋实现嘞,总不能在每个⽅法⾥⾯都写⼀段代码来保证数据结构的⼀致性吧,这样太傻了。通过这篇博客就搞⼀下怎样简洁的实现此功能。具体思路是这样的:
1、⾃定义⼀个注解@ExeResponse,凡是被这个注解标记的⽅法或者类都会返回标准化的数据格式,其余的都返回正常的数据格式。
2、通过反射机制获取被@ExeResponse注解标记的类或者⽅法进⽽进⾏数据封装。
3、封装完毕的数据结构返回到调⽤⽅。
2、划知识点
1、⾃定义注解
注解这个东西的使⽤,个⼈⽐较随意的理解就是打标记。把凡是被打过标记的类或者⽅法使⽤⼀定的⽅式(⽐如java的反射机制)进⾏集中进⾏处理。可能不太好理解,在下⾯的内容详细进⾏叙述⼀下,不熟悉这块的同学也可以⾃⾏了解。
2、@ConditionalOnBean注解
这个注解为条件注解,具体的⽤法为@ConditionalOnBean({ A.class })。只有A类被加载以后,被@ConditionalOnBean标记的类才会加载。
3、@ConditionalOnProperty注解
spring boot中通过控制配置⽂件的参数属性来控制@Configuration是否⽣效。具体⽤法往下看或者⾃⾏搜索
4、WebMvcConfigurer接⼝
spring boot2.0以后可以通过实现WebMvcConfigurer来⾃定义⼀些、页⾯跳转、视图解析器、信息转换器等⼀些骚操作。本次就通过⾃定义⼀个进⽽处理被⾃定义注解标记的类或者⽅法。
5、HandlerInterceptor接⼝
⾃定义我想⼤家都并不陌⽣了,最常⽤的登录拦截、或是权限校验等等,这次⽤于反射操作类或者⽅法。
6、ResponseBodyAdvice接⼝
这个接⼝⼀般⽤于对请求后数据结构的封装、加密等操作。
7、反射机制
官⽅的解释:JAVA反射机制是在运⾏状态中,对于任意⼀个类,都能够知道这个类的所有属性和⽅法;对于任意⼀个对象,都能够调⽤它的任意⼀个⽅法和属性;这种动态获取的信息以及动态调⽤对象的⽅法的功能称为java语⾔的反射机制。
的确是这个样⼦,java反射机制基本上可以说是⼀些架构的灵魂所在了,不太明⽩的同志可以下去好好研究⼀下。
3、核⼼代码实现
1、⾃定义@ExeResponse注解
public @interface ExeResponse {
Class<? extends Results> valus() default ExeResult.class;
}
2、⾃定义
@Component
//检查在配置⽂件中是否有abled参数,有并设置为true,注⼊ResponseResultInterceptor
@ConditionalOnProperty(name = { "abled" }, havingValue = "true", matchIfMissing = true)
public class ResponseResultInterceptor implements HandlerInterceptor{
public static final String RESPONSE_RESULT = "RESPONSE_RESULT";
public static final String REQUEST_ID = "request_Id";
private static final String REQUEST_TIME = "REQUEST_TIME";
private static final Logger LOGGER = Logger(ResponseResultInterceptor.class);
public ResponseResultInterceptor() {
LOGGER.info("abled use default value: true");
}
/**
* preHandle⽅法在业务处理器处理请求之前被调⽤,进⾏预处理
* 第⼀步根据访问的class或method判断是否被注解标记,如果是则放⼊HttpServletRequest中
*/
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
//当handle为HandlerMethod类或者⼦类创建的对象时
if (handler instanceof HandlerMethod) {
//handler转化为HandlerMethod
final HandlerMethod handlerMethod = (HandlerMethod)handler;
//获取此次访问的controller的class类
final Class<?> clazz = (Class<?>)BeanType();
//获取此次访问的method⽅法
final Method method = Method();
//判断method是否被IdcResponse注解标注
if (method.isAnnotationPresent(ExeResponse.class)) {
//如果method被IdcResponse注解标记,取出放⼊HttpServletRequest中
request.setAttribute(RESPONSE_RESULT, (Annotation(ExeResponse.class));
//记录时间戳
request.setAttribute(REQUEST_TIME, (Object)System.currentTimeMillis());
}
else if (clazz.isAnnotationPresent(ExeResponse.class)) {
//如果class被IdcResponse注解标记,取出放⼊HttpServletRequest中
request.setAttribute(RESPONSE_RESULT, (Annotation(ExeResponse.class));
request.setAttribute(REQUEST_TIME, (Object)System.currentTimeMillis());
}
}
return true;
}
/**
* postHandle⽅法在业务处理器处理请求执⾏完成后,⽣成视图之前执⾏
* 最后⼀步,打印此次访问的信息
*/
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) {
}
/**
* 在DispatcherServlet完全处理完请求后被调⽤,可⽤于数据返回处理
* 最后⼀步,打印此次访问的信息
*/
public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod)handler;
final Class<?> clazz = (Class<?>)BeanType();
final Method method = Method();
if (method.isAnnotationPresent(ExeResponse.class) || clazz.isAnnotationPresent(ExeResponse.class)) {
final String requestID = Header(REQUEST_ID);
final int statusCode = Status();
final long requestTime = (Attribute(REQUEST_TIME);
if (statusCode != HttpStatus.OK.value()) {
<("RequestID: {}, Method: {}, Response Time: {}ms, HttpStatus: {}", new Object[] { requestID, Name(), System.currentTimeMi }
}
else {
LOGGER.info("RequestID: {}, Method: {}, Response Time: {}ms", new Object[] { requestID, Name(), System.currentTimeMillis() - requestTi }
}
}
}
}
3、⾃定义实现ResponseBodyAdvice接⼝的功能类
@ControllerAdvice
springboot架构图//ResponseResultInterceptor存在的情况下才会把ResponseResultHandler注⼊
@ConditionalOnBean({ ResponseResultInterceptor.class })
public class ResponseResultHandler implements ResponseBodyAdvice<Object>{
public static final String RESPONSE_RESULT = "RESPONSE_RESULT";
public static final String REQUEST_ID = "request_Id";
private static final Logger LOGGER = Logger(ResponseResultHandler.class);
/**
* 第⼆步判断HttpServletRequest中是否有注解对象
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
//⽣成HttpServletRequest
HttpServletRequest hsr = Request();
//取出HttpServletRequest中的注解
Object obj = Attribute(RESPONSE_RESULT);
//转换成IdcResponse注解
final ExeResponse responseResultAnn = (ExeResponse)obj;
//不等于空返回true
return responseResultAnn != null;
}
/**
* 在数据返回之前更改格式
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//获取HttpServletRequest对象
final HttpServletRequest httpRequest = Request();
//取出IdcResponse注解类
final ExeResponse idcResponse = (Attribute(RESPONSE_RESULT);
//取出IdcResponse的valus,Results或者其⼦类
final Class<? extends Results> resultClazz = idcResponse.valus();
Object objBuffer = null;
try {
//ServerHttpResponse赋值,在ServerHttpRequest中取
//resultClazz实例化
final Results result = (wInstance();
//给Results赋值,body为查询到的数据
result.setCode(de());
result.setMsg(ssage());
result.setCompany(ExeResultCode.SUCCESSpany());
result.setData(body);
if (body instanceof String || selectedConverterType.isAssignableFrom(StringHttpMessageConverter.class)) {
objBuffer = Json();
}
else if (body instanceof Results) {
objBuffer = body;
}
else {
else {
objBuffer = result;
}
}
catch (InstantiationException | IllegalAccessException ex2) {
final ReflectiveOperationException ex = null;
final ReflectiveOperationException e = ex;
objBuffer = new ExeResult(ExeResultCode.SYSTEM_de(), e.getMessage()); ("BeforeBodyWrite append erro!", (Throwable)e);
}
return objBuffer;
}
}
4、⽤于测试的两个controller
/**
* 测试标记类
* @author Administrator
*
*/
@RestController
@ExeResponse
@RequestMapping(value = "/testclass")
public class ExeResponseClassController {
@RequestMapping(value = "/testResponseClass",method = RequestMethod.GET)
public String testResponseMethed() {
return "this is a test class";
}
}
@RestController
@RequestMapping(value = "/testmethod")
public class ExeResponseMethodController {
/**
* 测试标记⽅法
* @return
*/
@ExeResponse
@RequestMapping(value = "/testResponseMethod",method = RequestMethod.GET)
public String testResponseMethed() {
return "this is a test method";
}
/**
* 未标记的⽅法
* @return
*/
@RequestMapping(value = "/noResponseMethod",method = RequestMethod.GET)
public String noResponseMethod() {
return "this is a no response method";
}
}
4、启动调⽤
5、总结
以上为实现该功能的⼤致过程,简单的request、response的流程图如下
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论