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
//检查在配置文件中是否有exe-response.enabled参数,有并设置为true,注入ResponseResultInterceptor
@ConditionalOnProperty(name = { "exe-response.enabled" }, 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 = LoggerFactory.getLogger(ResponseResultInterceptor.class);
	
	public ResponseResultInterceptor() {
		LOGGER.info("exe-response.enabled 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<?>)handlerMethod.getBeanType();
            //获取此次访问的method方法
            final Method method = handlerMethod.getMethod();
            //判断method是否被IdcResponse注解标注
            if (method.isAnnotationPresent(ExeResponse.class)) {
            	//如果method被IdcResponse注解标记,取出放入HttpServletRequest中
                request.setAttribute(RESPONSE_RESULT, (Object)method.getAnnotation(ExeResponse.class));
                //记录时间戳
                request.setAttribute(REQUEST_TIME, (Object)System.currentTimeMillis());
            }
            else if (clazz.isAnnotationPresent(ExeResponse.class)) {
            	//如果class被IdcResponse注解标记,取出放入HttpServletRequest中
                request.setAttribute(RESPONSE_RESULT, (Object)clazz.getAnnotation(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<?>)handlerMethod.getBeanType();
	            final Method method = handlerMethod.getMethod();
	            if (method.isAnnotationPresent(ExeResponse.class) || clazz.isAnnotationPresent(ExeResponse.class)) {
	                final String requestID = request.getHeader(REQUEST_ID);
	                final int statusCode = response.getStatus();
	                final long requestTime = (long)request.getAttribute(REQUEST_TIME);
	                if (statusCode != HttpStatus.OK.value()) {
	                	LOGGER.error("RequestID: {}, Method: {}, Response Time: {}ms, HttpStatus: {}", new Object[] { requestID, method.getName(), System.currentTimeMillis() - requestTime, statusCode, ex });
	                }
	                else {
	                	LOGGER.info("RequestID: {}, Method: {}, Response Time: {}ms", new Object[] { requestID, method.getName(), System.currentTimeMillis() - requestTime });
	                }
	            }
	        }
	    }
	
}

3、自定义实现ResponseBodyAdvice接口的功能类

@ControllerAdvice
//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 = LoggerFactory.getLogger(ResponseResultHandler.class);
	/**
	 * 第二步判断HttpServletRequest中是否有注解对象
	 */
	@Override
	public boolean supports(MethodParameter returnType, Class converterType) {
		//生成HttpServletRequest
		HttpServletRequest hsr = HttpServletUtils.getRequest();
		//取出HttpServletRequest中的注解
		Object obj = hsr.getAttribute(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 = HttpServletUtils.getRequest();
		//取出IdcResponse注解类
        final ExeResponse idcResponse = (ExeResponse)httpRequest.getAttribute(RESPONSE_RESULT);
        //取出IdcResponse的valus,Results或者其子类
        final Class<? extends Results> resultClazz = idcResponse.valus();
        Object objBuffer = null;
        try {
        	//ServerHttpResponse赋值,在ServerHttpRequest中取
            HttpServletUtils.getResponse().setHeader(REQUEST_ID, httpRequest.getHeader(REQUEST_ID));
            //resultClazz实例化
            final Results result = (Results)resultClazz.newInstance();
            //给Results赋值,body为查询到的数据
            result.setCode(ExeResultCode.SUCCESS.code());
            result.setMsg(ExeResultCode.SUCCESS.message());
            result.setCompany(ExeResultCode.SUCCESS.company());
            result.setData(body);
            if (body instanceof String || selectedConverterType.isAssignableFrom(StringHttpMessageConverter.class)) {
                objBuffer = result.toJson();
            }
            else if (body instanceof Results) {
                objBuffer = body;
            }
            else {
                objBuffer = result;
            }
        }
        catch (InstantiationException | IllegalAccessException ex2) {
            final ReflectiveOperationException ex = null;
            final ReflectiveOperationException e = ex;
            objBuffer = new ExeResult(ExeResultCode.SYSTEM_INNER_ERROR.code(), e.getMessage());
            ResponseResultHandler.LOGGER.error("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的流程图如下

具体的源码下载链接https://download.csdn.net/download/lw1124052197/12800131,由于个人C币已经不太够用,下载会象征性收几个C币,见谅,不喜勿喷。

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SpringBoot是一个快速构建应用的框架,可以通过注解方式快速搭建自己所需的web应用程序。自定义response注解是在SpringBoot开展中很常见的任务,在返回结果前对数据的做出处理或修饰。在SpringBoot中,通过自定义response注解可以简化一些对返回结果的处理,开发者可以通过自定义注解的方式对返回结果进行处理和修饰。因此,自定义response注解可以为开发者带来很多的便利性和优势。 自定义response注解的实现可以分为以下步骤: 1.编写注解类:在Java中定义一个注解需要使用 @interface 关键字定义新注解,同时在注解中定义成员变量,如下所示: @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomResponse { } 2.添加自定义注解:在需要对返回结果进行处理的方法上标注我们定义的注解。 @GetMapping("/hello") @CustomResponse public String hello() { return "Hello World"; } 3.写注解处理器:定义一个自定义的AOP切面,通过对注解进行判断来对返回结果进行处理。 @Aspect @Component public class CustomResponseAspect { @AfterReturning(value = "@annotation(com.example.demo.annotation.CustomResponse)", returning = "result") public Object afterReturning(JoinPoint joinPoint, Object result) { if (result instanceof CommonResponse) { return result; } else { return new CommonResponse<>(result); } } } 4.测试:启动SpringBoot应用程序,浏览器输入请求地址,可以看到自定义注解的处理效果。 总之,SpringBoot框架提供了强大的功能支持,自定义response注解可以实现对数据的处理和修饰,为开发者提供了便捷性和优良的程序设计方式。而且,通过注解方式实现处理会更加简单,更能节省开发人员的时间和精力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值