你可能会有这样的需求,对你的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; } } } }