GVKun编程网logo

【原创】遨游springmvc之HandlerMethodReturnValueHandler(遨游官网)

19

以上就是给各位分享【原创】遨游springmvc之HandlerMethodReturnValueHandler,其中也会对遨游官网进行解释,同时本文还将给你拓展2.盘点springmvc的常用接口之

以上就是给各位分享【原创】遨游springmvc之HandlerMethodReturnValueHandler,其中也会对遨游官网进行解释,同时本文还将给你拓展2.盘点springmvc的常用接口之HandlerMethodReturnValueHandler、org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler的实例源码、org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod的实例源码、ResourceHttpRequestHandler——SpringMVC静态资源发布时使用的处理器handler等相关知识,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

【原创】遨游springmvc之HandlerMethodReturnValueHandler(遨游官网)

【原创】遨游springmvc之HandlerMethodReturnValueHandler(遨游官网)

1.前言

在springmvc中,很多人都知道@ResponseBody,加了它那就会返回对应的数据结果(json),而不是一张jsp或者其他视图。如果不加,那么它就返回了一个具体的视图,如jsp。那么让我们来深入探析下这里面的究竟。

在前一篇我们主要讲述了springmvc对传入参数的接口HandlerMethodArgumentResolver。那么springmvc同样也也提供了一系列对响应返回值进行处理的接口,核心接口类就是本篇要介绍的HandlerMethodReturnValueHandler。

2.原理

2.1 接口说明

public interface HandlerMethodReturnValueHandler {

	/**
	 * Whether the given {@linkplain MethodParameter method return type} is
	 * supported by this handler.
	 * @param returnType the method return type to check
	 * @return {@code true} if this handler supports the supplied return type;
	 * {@code false} otherwise
	 */
	boolean supportsReturnType(MethodParameter returnType);

	/**
	 * Handle the given return value by adding attributes to the model and
	 * setting a view or setting the
	 * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
	 * to indicate the response has been handled directly.
	 * @param returnValue the value returned from the handler method
	 * @param returnType the type of the return value. This type must have
	 * previously been passed to {@link #supportsReturnType} which must
	 * have returned {@code true}.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @throws Exception if the return value handling results in an error
	 */
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

HandlerMethodReturnValueHandler是在spring3.1之后引入的

它主要包含2个方法:

1.supportsReturnType()决定了哪类类型的返回值将启用该返回值处理器

2.handleReturnValue则是主要处理返回值的处理逻辑,并且将处理好的值返回给model,还可以处理该返回什么视图

 

2.2 依赖

HandlerMethodReturnValueHandler的实现大多类都是已ReturnValueHandler或者Processor(包含了参数处理)结尾

 

2.3 源码分析

2.3.1 初始化HandlerMethodReturnValueHandler

在初始化RequestMappingHandlerAdapter时候,springmvc默认初始化了一系列返回值处理器,并且提供了自动以的HandlerMethodReturnValueHandler的入口。

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types 加入自定义的返回值处理器
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

 

HandlerMethodReturnValueHandler的匹配是按照初始化的顺序,请看下面table罗列的处理器和对应的处理类型。

处理器 处理类型
针对一中类型
ModelAndViewMethodReturnValueHandler ModelAndView
ModelMethodProcessor Model
ViewMethodReturnValueHandler View
ResponseBodyEmitterReturnValueHandler ResponseEntity<ResponseBodyEmitter>
StreamingResponseBodyReturnValueHandler ResponseEntity<StreamingResponseBody>
HttpHeadersReturnValueHandler HttpHeaders
 CallableMethodReturnValueHandler  Callable
DeferredResultMethodReturnValueHandler DeferredResult、ListenableFuture、CompletionStage
AsyncTaskMethodReturnValueHandler WebAsyncTask
针对注解
ModelAttributeMethodProcessor @ModelAttribute(require=false)
RequestResponseBodyMethodProcessor @ResponseBody
处理多种类型
ViewNameMethodReturnValueHandler void、CharSequence(V4.2)
MapMethodProcessor Map
自定义返回值处理器
ModelAndViewResolverMethodReturnValueHandler 默认处理,如果以上的都不满足就会进入
ModelAttributeMethodProcessor @ModelAttribute(require=true)

 

2.3.2 RequestResponseBodyMethodProcessor

public boolean supportsReturnType(MethodParameter returnType) {
//支持类型
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

mavContainer.setRequestHandled(true);//请求是否已经完全在处理程序中处理好,如果是fasle,则继续流转到对应的视图,如果设置为true,则不会再进过视图这一 层直接响应,默认是false。

public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);//如果没有视图,则必须设置为true,否则会返回视图层
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
        //输出
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

 

3.实例

我们来实现一个类似@ResponseBody的功能。

3.1 KingsResponseBody

@Target ( {ElementType.TYPE, ElementType.METHOD})
@Retention (RetentionPolicy.RUNTIME)
@Documented
public @interface KingsResponseBody {
    String type() default "json";
}

 

3.2 CustomerHandlerMethodReturnValueHandler

public class CustomerHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), KingsResponseBody.class) || returnType.hasMethodAnnotation(KingsResponseBody.class));
    }
    
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        KingsResponseBody anno = returnType.getMethodAnnotation(KingsResponseBody.class);
        mavContainer.setRequestHandled(true);
        String type = anno.type();
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.setContentType("text/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            Gson jb = new Gson();
            out.write(jb.toJson(returnValue));
            out.flush();
        } catch (IOException e) {
            throw e;
        }
    }
}

有时候我们还可以往响应头里面丢一些内容什么的,具体的实现根据需求而定。

 

3.3 配置

    <mvc:annotation-driven >
        <mvc:return-value-handlers>
            <bean/>
        </mvc:return-value-handlers>
    </mvc:annotation-driven>

 

3.4 控制器

@Controller
public class HandlerMethodReturnValueHandlerDemoController {
    
    @RequestMapping (value="/returnvalue/1",method=GET)
    @KingsResponseBody
    public List<Person> demo() {
        Person p1 = new Person();
        p1.setName("WS");
        Person p2 = new Person();
        p2.setName("Kings");
        return Lists.newArrayList(p1,p2);
    }
}

 

访问:http://localhost:8080/returnvalue/1

返回:

[{"name":"WS"},{"name":"Kings"}]

 

4. 总结

当我们需要统一处理springmvc返回值的时候我们就可以考虑使用HandlerMethodReturnValueHandler,而且也非常简单,比如某些场景会需要我们在响应里面加入特定一些什么响应头或者需要转化为指定格式。

 

发现一个机制的导航

2.盘点springmvc的常用接口之HandlerMethodReturnValueHandler

2.盘点springmvc的常用接口之HandlerMethodReturnValueHandler

2.盘点springmvc的常用接口之HandlerMethodReturnValueHandler###

前言

上一章中介绍了HandlerMethodArgumentResolver来处理controller方法的参数绑定问题。一个请求中有入参的绑定处理,那么当然也会有响应参数的处理,这一章就来讲讲处理返回对象的接口——org.springframework.web.method.support.HandlerMethodReturnValueHandler

接口说明

public interface HandlerMethodReturnValueHandler {
	//检验是否支持本处理器处理,返回true会执行handleReturnValue
	boolean supportsReturnType(MethodParameter returnType);
  
	//处理返回值的方法,returnValue即是controller方法中的返回值
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

示例1

controller

package com.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.demo.domain.Person;

@Controller
@RequestMapping("demo2")
public class HandlerMethodReturnValueHandlerDemoController {

	@ResponseBody
	@RequestMapping(method = RequestMethod.GET)
	public Person getPerson() {
		Person person = new Person("Bill", "Gates");
		return person;
	}
}

这个controller方法中返回了Person对象,如果使用默认的配置,将会把Person对象以json格式输出。

{
  "firstName": "Bill",
  "lastName": "Gates"
}

那是因为使用了@ResponseBody注解,springmvc调用了RequestResponseBodyMethodProcessor,而RequestResponseBodyMethodProcessor实现了HandlerMethodReturnValueHandler接口的handleReturnValue方法。

@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
  throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

  mavContainer.setRequestHandled(true);

  // Try even with null return value. ResponseBodyAdvice could get involved.
  writeWithMessageConverters(returnValue, returnType, webRequest);
}

看源码可知,RequestResponseBodyMethodProcessor转交给了HttpMessageConverter去处理,具体的是转交给了MappingJackson2HttpMessageConverter,使用Jackson框架把person对象序列化成了json。

实现自己的HandlerMethodReturnValueHandler

package com.demo.mvc.component;

import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.demo.domain.Person;

public class PersonReturnValueHandler implements HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return returnType.getParameterType() == Person.class;
	}

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		Assert.isInstanceOf(Person.class, returnValue);
		mavContainer.setRequestHandled(true);
		HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
		response.setContentType("text/plain;charset=utf-8");
      	Person person = (Person) returnValue;
		response.getWriter().write("经过HandlerMethodReturnValueHandler输出的person:" + person);
	}
}

其中mavContainer.setRequestHandled(true);标志着此次请求是否是由handler自己控制的,true表示本方法会响应请求。

使用自己的实现后,需要去除controller方法上的@ResponseBody注解,因为我们不再需要使用默认的处理器。

@RequestMapping(method = RequestMethod.GET)
public Person getPerson() {
  Person person = new Person("Bill", "Gates");
  return person;
}

发送请求GET http://localhost:8080/demo2

响应得到

经过HandlerMethodReturnValueHandler输出的person:Bill Gates

示例2

发挥想象,给controller的方法加上自定义注解辅助返回值响应。

设置Person前缀注解:

package com.demo.mvc.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonPrefix {

	public String value() default "";
}

controller

@PersonPrefix("rich man")
@RequestMapping(method = RequestMethod.GET)
public Person getPerson() {
  Person person = new Person("Bill", "Gates");
  return person;
}

handleReturnValue方法:

@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  Assert.isInstanceOf(Person.class, returnValue);
  mavContainer.setRequestHandled(true);
  // 获取方法上的注解
  PersonPrefix annotation = returnType.getMethodAnnotation(PersonPrefix.class);
  HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
  response.setContentType("text/plain;charset=utf-8");
  Person person = (Person) returnValue;
  response.getWriter().write(annotation.value() + " : " + person);
}

最后响应:

rich man : Bill Gates

友情链接:

盘点springmvc的常用接口目录

org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler的实例源码

org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler的实例源码

项目:grpc-java-contrib    文件:GuavaLFReturnValueHandler.java   
public GuavaLFReturnValueHandler install(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
    final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(
            requestMappingHandlerAdapter.getReturnValueHandlers());

    final int deferredPos = indexOfType(originalHandlers,DeferredResultMethodReturnValueHandler.class);
    // Add our handler directly after the deferred handler.
    originalHandlers.add(deferredPos + 1,this);

    requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers);

    return this;
}
项目:grpc-java-contrib    文件:GuavaLFReturnValueHandlerTest.java   
@Test
public void handlerIsAfterDeferredResultMethodReturnValueHandler() {
    List<HandlerMethodReturnValueHandler> handlers = requestMappingHandlerAdapter.getReturnValueHandlers();
    int lfHandlerIndex = indexOfType(handlers,GuavaLFReturnValueHandler.class);
    int drHandlerIndex = indexOfType(handlers,DeferredResultMethodReturnValueHandler.class);

    assertthat(lfHandlerIndex).isGreaterThan(drHandlerIndex);
}
项目:spring-boot-mvc-CompletableFuture    文件:WebMvcConfiguration.java   
@postconstruct
public void init() {
  final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(
      requestMappingHandlerAdapter.getReturnValueHandlers());

  final int deferredPos = obtainValueHandlerPosition(originalHandlers,DeferredResultMethodReturnValueHandler.class);
  // Add our handler directly after the deferred handler.
  originalHandlers.add(deferredPos + 1,CompletableFutureReturnValueHandler());

  requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers);
}

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod的实例源码

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod的实例源码

项目:nbone    文件:WebExceptionResolver.java   
protected ServletinvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod,Exception exception)
{
  Class<?> handlerType = handlerMethod.getBeanType();
  Method method = getExceptionHandlerMethodResolver(handlerType).resolveMethod(exception);

  if (method != null) {
    return new ServletinvocableHandlerMethod(handlerMethod.getBean(),method);
  }
  try
  {
    method = getClass().getmethod("getFailedResult",new Class[] { Exception.class });
  }
  catch (Exception e) {
  }
  return new ServletinvocableHandlerMethod(this,method);
}
项目:leopard    文件:invocableHandlerMethodCreatorImpl.java   
@Override
public ServletinvocableHandlerMethod createinvocableHandlerMethod(HandlerMethod handlerMethod) {
    ServletinvocableHandlerMethod invocableHandlerMethod;
    if (creator != null) {
        invocableHandlerMethod = creator.createinvocableHandlerMethod(handlerMethod);
        if (invocableHandlerMethod != null) {
            return invocableHandlerMethod;
        }
    }
    return new ServletinvocableHandlerMethod(handlerMethod);
}
项目:acceptance    文件:VCSRepositoryCRUDControllerTest.java   
private ExceptionHandlerExceptionResolver createExceptionResolver() {
  ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
    protected ServletinvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod,Exception exception) {
      Method method = new ExceptionHandlerMethodResolver(JsonErrorHandler.class).resolveMethod(exception);
      return new ServletinvocableHandlerMethod(jsonErrorHandler,method);
    }
  };
  List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
  messageConverters.add(new FormHttpMessageConverter());
  messageConverters.add(new StringHttpMessageConverter());
  messageConverters.add(new MappingJackson2HttpMessageConverter());
  exceptionResolver.setMessageConverters(messageConverters);
  exceptionResolver.afterPropertiesSet();
  return exceptionResolver;
}
项目:acceptance    文件:CredentialCRUDControllerTest.java   
private ExceptionHandlerExceptionResolver createExceptionResolver() {
  ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
    protected ServletinvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod,method);
    }
  };
  List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
  messageConverters.add(new FormHttpMessageConverter());
  messageConverters.add(new StringHttpMessageConverter());
  messageConverters.add(new MappingJackson2HttpMessageConverter());
  exceptionResolver.setMessageConverters(messageConverters);
  exceptionResolver.afterPropertiesSet();
  return exceptionResolver;
}
项目:leopard    文件:LeopardRequestMappingHandlerAdapter.java   
@Override
protected ServletinvocableHandlerMethod createinvocableHandlerMethod(HandlerMethod handlerMethod) {
    return invocableHandlerMethodCreator.createinvocableHandlerMethod(handlerMethod);
}
项目:leopard    文件:invocableHandlerMethodCreator.java   
ServletinvocableHandlerMethod createinvocableHandlerMethod(HandlerMethod handlerMethod);

ResourceHttpRequestHandler——SpringMVC静态资源发布时使用的处理器handler

ResourceHttpRequestHandler——SpringMVC静态资源发布时使用的处理器handler

我们知道,拦截器是SpringMVC提供的一种AOP的实现,而SpringMVC体系默认是只有dispatcherServlet一个Servlet的,所以拦截器并不能拦截自定义Servlet的情况(虽然我们自定义的Servlet可以有处理请求的功能)。
SpringMVC讲究所有网络都由handler提供,所以,我们尝试了一下静态资源发布的时候,到底用的是哪些handler。

我们通过拦截器方法中给出的handler,也就是像这样:

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("handler:" + handler);
    }

首先,静态资源目录,也就是classpath,是可以直接通过路径关系去访问的,这时候我们访问资源,输出信息,得到其handler是:

handler:ResourceHttpRequestHandler [classpath [meta-inf/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]

然后我们再尝试Controller发布静态网页:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class PageController {

    @GetMapping("/page_3")
    public String index() {
        return "page_3";
    }
}

得到的结果是:

handler:com.micah.demo.controller.PageController#index()

我们知道,有些静态资源放在一些冷门的路径中,这时我们可以自己去定义资源路径。

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        System.out.println("addResourceHandlers...");
        registry.addResourceHandler("/d/**").addResourceLocations("file:D:/");
    }
}

这时候我们访问d盘的一个资源,得到的处理器handler信息:

handler:ResourceHttpRequestHandler [URL [file:D:/]]

我们用自定义Servlet去发布静态资源,连dispatcherServlet都用不上,更别说用了什么handler了。

值得注意的是,加入我们将拦截器postHandle方法中的ModelAndView(未解析的view)打印,会得到一个更加有趣的结果,也就是只有当请求的静态资源是通过Controller获取的时候,ModelAndView才不为null,打印结果如下:

modelAndView:ModelAndView [view="page_3"; model={}]

也就是view为当时return的那个字符串。

关于【原创】遨游springmvc之HandlerMethodReturnValueHandler遨游官网的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于2.盘点springmvc的常用接口之HandlerMethodReturnValueHandler、org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler的实例源码、org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod的实例源码、ResourceHttpRequestHandler——SpringMVC静态资源发布时使用的处理器handler的相关信息,请在本站寻找。

本文标签: