两种方式,推荐第二种
第一种:添加自定义 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 处理
* 所以在此处进行自己的包装处理,或者完全自定义自己的返回值处理
* 将自己定义的处理器提前
*/
@Configuration
public class RestReturnValueHandlerConfigurer implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@Override
public 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;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return proxyObject.supportsReturnType(returnType);
}
/**
* 此处会根据设置的白名单进行数据的代理包装
* @param returnValue
* @param returnType
* @param mavContainer
* @param webRequest
* @throws Exception
*/
@Override
public 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;
/**
* 通过判断包名来表明是否支持统一包装返回数据
*/
@ControllerAdvice
public class ResponseAdvisor implements ResponseBodyAdvice<Object>, ApplicationContextAware {
//保存需要代理的包
private List<String> basePackage = new ArrayList<>();
@Override
public 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;
}
@Override
public 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
*/
@Override
public 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());
}
}
}
}