你可能會有這樣的需求,對你的Controller返回值進行一個二次封裝,
如下:code是結果碼(1、成功,0、失敗,2、未登錄...),data為攜帶數據
{"code":"1","data":{"name":"xiaoming","age":"30"}}
在代碼中的使用效果如下,添加一個自定義注解ResponseData,自動地就把數據封裝成上面的格式
@Controller public class Core { @ResponseData @RequestMapping("/data") public Map<String, Object> data() { Map<String,Object> res = new HashMap<>(); res.put("name","xiaoming"); res.put("age","30"); return res; } }
HandlerMethodReturnValueHandler
這個接口的名字有點怪,處理函數返回值的處理器?姑且叫他返回值處理器吧,
這是一個處理Controller返回值的接口,比如我們熟悉的ResponseBody注解,使用下面的代碼就能簡單地實現,
這個切面的缺點是:如果函數沒有返回值時,這個接口的代碼就不會執行,如果通過其他方式傳遞返回值,用這個接口會覺得不習慣。
import com.alibaba.druid.support.json.JSONUtils; import org.springframework.core.MethodParameter; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletResponse; /** * Created by 12614 on 2018/5/11. */ public class Example implements HandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter methodParameter) { return methodParameter.hasMethodAnnotation(ResponseBody.class); } @Override public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest) throws Exception { //TODO .setRequestHandled(true)表示此函數可以處理請求,不必交給別的代碼處理 //E.G. modelAndViewContainer.setRequestHandled(true); nativeWebRequest .getNativeResponse(HttpServletResponse.class) .getWriter() .write(JSONUtils.toJSONString(o)); } }
Spring配置
<mvc:annotation-driven> <mvc:return-value-handlers> <bean class="xxxx全類名"></bean> </mvc:return-value-handlers> </mvc:annotation-driven>
SpringBoot配置
因為在SpringBoot下,默認的ResponseBody處理類就是HandlerMethodReturnValueHandler,此處理器的優先級比我們新添加的實現類高,
在WebMvcConfigurerAdapter 中配置HandlerMethodReturnValueHandler,代碼其實是並不生效,
如果你希望添加額外的返回值處理器,需要做的是改造默認的處理類(如果直接替換默認的處理類,原先的ResponseBody的處理邏輯將出錯)。
import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import java.util.List; /** * Created by 12614 on 2018/5/11. */ @Configuration public class ApplicationConfigurer extends WebMvcConfigurerAdapter { @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { super.addReturnValueHandlers(returnValueHandlers); returnValueHandlers.add(new Example()); } }
RequestResponseBodyMethodProcessor是ResponseBody的默認處理程序,如果不是十分清楚它的作用,不希望改動任何默認處理邏輯,
可以建立一個靜態代理類,代碼如下:
import org.springframework.core.MethodParameter; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.HashMap; import java.util.Map; /** * Created by 12614 on 2018/5/11. */ public class TestReturnValueHandler implements HandlerMethodReturnValueHandler { private RequestResponseBodyMethodProcessor target; public TestReturnValueHandler(RequestResponseBodyMethodProcessor target) { this.target = target; } @Override public boolean supportsReturnType(MethodParameter methodParameter) { System.out.println("TestReturnValueHandler:supportsReturnType"); //我添加了一個自定義注解ResponseData,不知道怎么寫可以直接復制ResponseBody源碼 return methodParameter.hasMethodAnnotation(ResponseData.class)|| methodParameter.hasMethodAnnotation(ResponseBody.class); } @Override public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception { System.out.println("TestReturnValueHandler:handleReturnValue"); if(methodParameter.hasMethodAnnotation(ResponseData.class)){ //如果Controller中使用了我的自定義注解,那么對返回值進行封裝 Map<String,Object> res = new HashMap<>(); res.put("code","1"); res.put("data",o); target.handleReturnValue(res,methodParameter,modelAndViewContainer,nativeWebRequest); } else { target.handleReturnValue(o,methodParameter,modelAndViewContainer,nativeWebRequest); } } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return target.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } }
替換SpringBoot中HandlerMethodReturnValueHandler的默認實現類,需要在Spring中找到合適的切面,
InitializingBean在執行完所有屬性設置方法(即setXxx)將被自動調用,正好滿足我們的需求,在InitializingBean中編輯我們的代碼即可,代碼如下:
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; /** * 初始化切面 * * Created by 12614 on 2018/5/11. */ @Configuration public class InitializingAdvice implements InitializingBean { @Autowired private RequestMappingHandlerAdapter adapter; @Override public void afterPropertiesSet() throws Exception { List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers(); List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers); this.decorateHandlers(handlers); adapter.setReturnValueHandlers(handlers); } private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) { for (HandlerMethodReturnValueHandler handler : handlers) { if (handler instanceof RequestResponseBodyMethodProcessor) { TestReturnValueHandler decorator = new TestReturnValueHandler( (RequestResponseBodyMethodProcessor) handler); int index = handlers.indexOf(handler); handlers.set(index, decorator); break; } } } }