两种方式,推荐第二种
第一种:添加自定义 HandlerMethodReturnValueHandler
原理:spring mvc 的数据处理流程会经过 HandlerMethodReturnValueHandler 的实现方式进行一次数据处理
import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.method.support.HandlerMethodReturnValueHandler;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;import java.util.ArrayList;import java.util.List;/*** spring mvc 在controller处理后会进行下一步 HandlerMethodReturnValueHandler 处理* 所以在此处进行自己的包装处理,或者完全自定义自己的返回值处理* 将自己定义的处理器提前*/@Configurationpublic class RestReturnValueHandlerConfigurer implements InitializingBean {@Autowiredprivate RequestMappingHandlerAdapter handlerAdapter;@Overridepublic void afterPropertiesSet() throws Exception {List<HandlerMethodReturnValueHandler> list = handlerAdapter.getReturnValueHandlers();List<HandlerMethodReturnValueHandler> newList = new ArrayList<>();if (null != list) {for (HandlerMethodReturnValueHandler valueHandler : list) {if (valueHandler instanceof RequestResponseBodyMethodProcessor) {HandlerMethodReturnValueHandlerProxy proxy = new HandlerMethodReturnValueHandlerProxy(valueHandler);newList.add(proxy);} else {newList.add(valueHandler);}}}handlerAdapter.setReturnValueHandlers(newList);}}
import org.springframework.core.MethodParameter;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.HandlerMethodReturnValueHandler;import org.springframework.web.method.support.ModelAndViewContainer;/*** 自定义自己的处理方式,此处采用替换系统内的 RequestResponseBodyMethodProcessor实现*/public class HandlerMethodReturnValueHandlerProxy implements HandlerMethodReturnValueHandler {private static final int STATUS_CODE_SUCCEEDED = 200;//返回对象处理方法所在类的白名单private String[] whiteNames = new String[]{"org.springdoc."};private HandlerMethodReturnValueHandler proxyObject;public HandlerMethodReturnValueHandlerProxy(HandlerMethodReturnValueHandler proxyObject) {this.proxyObject = proxyObject;}@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return proxyObject.supportsReturnType(returnType);}/*** 此处会根据设置的白名单进行数据的代理包装* @param returnValue* @param returnType* @param mavContainer* @param webRequest* @throws Exception*/@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws Exception {boolean isProxy = true;for (String whiteName : whiteNames) {if (returnType.getDeclaringClass().getName().contains(whiteName)) {isProxy = false;break;}}if (isProxy) {R hs = new R(returnValue);proxyObject.handleReturnValue(hs, returnType, mavContainer, webRequest);} else {proxyObject.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}}}
第二种
推荐原因
若是采用第一种的话,一些第三方定义的端点接口(例如oauth2的认证、actuator 的端点访问)也会被代理,导致返回的数据不能被其自身组件正确处理,很多时候我们也只是需要处理我们自己业务包内的数据。而第二种方案能够很好的处理这种情况,避免第三方组件使用出现问题。
原理:spring mvc 的数据处理流程经过 HandlerMethodReturnValueHandler 后会进行数据HttpMessageConverter的转换处理. 所以我们在此处进行处理,在spring boot 中通过 实现ResponseBodyAdvice
import com.boya.tancms.common.response.R;import org.springframework.beans.BeansException;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.ArrayList;import java.util.List;import java.util.Map;/*** 通过判断包名来表明是否支持统一包装返回数据*/@ControllerAdvicepublic class ResponseAdvisor implements ResponseBodyAdvice<Object>, ApplicationContextAware {//保存需要代理的包private List<String> basePackage = new ArrayList<>();@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {String controllerName = returnType.getDeclaringClass().getPackage().getName();if (controllerName != null && controllerName.length() > 0) {for (String p : basePackage) {if (controllerName.startsWith(p)) {return true;}}}return false;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {//数据包装return new R(body);}/*** 通过从{@link SpringBootApplication#scanBasePackages}接口中获取需要实现数据统一包装的业务包* @param applicationContext* @throws BeansException*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {//获取有NotAutowired2Service注解的实例Map<String, Object> beansWithAnnotationMap = applicationContext.getBeansWithAnnotation(SpringBootApplication.class);Class<? extends Object> clazz = null;for (Map.Entry<String, Object> entry : beansWithAnnotationMap.entrySet()) {clazz = entry.getValue().getClass();//获取到实例对象的class信息SpringBootApplication annotation = clazz.getAnnotation(SpringBootApplication.class);String[] basePackageArray = annotation.scanBasePackages();if (basePackageArray.length < 1) {basePackage.add(clazz.getPackage().getName());}}}}
