springMvc之HandlerMethodArgumentResolver & HandlerMethodReturnValueHandler

org.springframework.web.method.support.HandlerMethodArgumentResolver

package org.springframework.web.method.support;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;

public interface HandlerMethodArgumentResolver {
	//用於斷定是否須要處理該參數分解,返回true爲須要,並會去調用下面的方法resolveArgument。
	boolean supportsParameter(MethodParameter parameter);
	//真正用於處理參數分解的方法,返回的Object就是controller方法上的形參對象。
	Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

springmvc自帶的一些實現:java

  • ServletRequestMethodArgumentResolverServletResponseMethodArgumentResolver處理了自動綁定HttpServletRequest和HttpServletResponse
  • RequestParamMapMethodArgumentResolver處理了@RequestParam
  • RequestHeaderMapMethodArgumentResolver處理了@RequestHeader
  • PathVariableMapMethodArgumentResolver處理了@PathVariable
  • ModelAttributeMethodProcessor處理了@ModelAttribute
  • RequestResponseBodyMethodProcessor(實際處理類AutoWrapperRequestResponseBodyMethodProcessor)處理了@RequestBody 。同時也繼承: org.springframework.web.method.support.HandlerMethodReturnValueHandler 用來處理 @ResponseBody

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;

常見如:AutoWrapperRequestResponseBodyMethodProcessor處理了@RequestBody  web

執行鏈路

Servlet.service -> DispatcherServlet.doDispatch -> AbstractHandlerMethodAdapter.handle -> RequestMappingHandlerAdapter.handleInternal -> ServletInvocableHandlerMethod.invokeAndHandle:spring

  1. 從集合argumentResolvers選中惟一的HandlerMethodArgumentResolver處理!
  2. 從集合returnValueHandlers選中惟一的HandlerMethodReturnValueHandler處理!

因此:  使用自定義實現後,須要去除controller方法上的@ResponseBody註解(或類上的@RestController 註解換爲 @Controller),否則由於自定義的優先級比較低,仍是會走默認的處理器!api

mavContainer.setRequestHandled(true)  必定要!

 

標識請求是否已經在該方法內完成處理mvc

在RequestMappingHandlerAdapter.invokeHandlerMethod執行過程當中,在處理完輸出對象後, 會調用getModelAndView子過程經過「mavContainer.isRequestHandled()」來斷定返回一個ModelAndeView對象。
app

正確的邏輯應該返回null;若返回了ModelAndView對象,但內部屬性View爲空也無法解析。
ide

DispatcherServlet.doDispatch -> DispatcherServlet.processDispatchResult -> DispatcherServlet.render -> InternalResourceView(AbstractView).render 解析過程輸出異常 :spring-boot

2019-12-12 17:42:00.613 [http-nio-8023-exec-9] WARN  org.springframework.web.servlet.PageNotFound -- No mapping found for HTTP request with URI [/api/api/query] in DispatcherServlet with name 'dispatcherServlet'

postMan響應結果:
post

自定義實現

參數及返回結果處理spa

import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.utrusts.adapter.dto.guarantee.Person;

public class SessionArgumentsReturnValRelover
		implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(Session.class) && parameter.getParameterType().equals(String.class);
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		return "test";
	}

	@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 {
		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);
	}

}

spring-boot方式註冊自定義分解器

@Configuration
public class MyConfigurer extends WebMvcConfigurationSupport {
	SessionArgumentsReturnValRelover relover = new SessionArgumentsReturnValRelover();

	@Override
	protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		argumentResolvers.add(relover);
	}

	@Override
	protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		returnValueHandlers.add(relover);
	}
}

使用時要注意的是: 使用自定義實現後,須要去除controller方法上的@ResponseBody註解(或類上的@RestController 註解換爲 @Controller),否則由於自定義的優先級比較低,仍是會走默認的處理器。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.utrusts.adapter.aop.Session;
import com.utrusts.adapter.dto.common.query.QueryApplyResultReq;
import com.utrusts.adapter.dto.guarantee.Person;

import lombok.extern.slf4j.Slf4j;
//@RestController
@Controller
@RequestMapping("/api")
public class QueryController {

	@PostMapping("/query")
	public Person query(@RequestBody QueryApplyResultReq queryReq, @Session String user) {
		Person person = new Person();
		person.setName(user);
		return person ;
	}
}

postMan響應結果:

相關文章
相關標籤/搜索