spring MVC處理請求過程及配置詳解


本文主要梳理下Spring MVC處理http請求的過程,以及配置servlet及業務application需要的常用標簽,及其包含的意義。

spring MVC處理請求過程

首先看一個整體圖
結構圖

簡單說下各步驟:

handlerMapping

handlerMapping將請求映射到處理器,即圖中的HandlerExecutionChain。依據是請求中的信息:請求URL(value),請求參數(params),請求方法(method),請求頭(headers)。處理器方法從中獲取參數,相關的標注有PathVariable、RequestParam、RequestHeader、CookieValue等。

  • 請求方法包括GET、POST、DELETE、HEAD、OPTIONS、PUT、TRACE等。大多數瀏覽器只支持GET和POST,解決方法:客戶端post提交數據,添加“_method”參數來指定特定的方法;服務端配置HiddenHttpMethodFilter。spring會根據_method的值模擬特定的HTTP方法,從而被controller正確獲取。

DataBinder

DataBinder處理servletRequest中的消息,對其進行數據類型轉換(conversionService接口)和數據格式化(Formatter接口)操作,然后填充到入參對象中。再調用validator組件,做數據校驗。把conversion和validator的結果放在BindingResult中。即BindingResult存儲入參對象和校驗錯誤對象,可直接作為controller處理方法的參數。

看一下ConfigurableWebBindingInitializer(封裝了WebDataBinder,繼承自DataBinder)的代碼:

	//處理BindingResult
	private BindingErrorProcessor bindingErrorProcessor;
	//數據校驗
	private Validator validator;
	//數據類型轉換
	private ConversionService conversionService;
  • 這里的convert做什么工作呢?比如controller處理方法的參數為User,包含用戶名、密碼、昵稱等,而用戶傳入的參數是userName:password:nick這樣的特定格式,這時就需要一個converter處理String2User。

HandlerExecutionChain

HandlerExecutionChain,執行鏈。包含一個處理器Handler(controller中的處理方法)及若干攔截器HandlerInterceptor。處理過程如下:

HandlerExecutionChain結構

  • handlerInterceptor如果處理出錯,就會直接返回結果,而不會到達handler。
  • 在進入handler之前,執行handlerInterceptor的preHandler方法;handler處理之后,執行handlerInterceptor的postHandler方法;相應被渲染后,執行handlerInterceptor的afterCompletion方法。

viewResolve

視圖對象是一個Bean,視圖對象由視圖解析器負責實例化(感覺有點像handler和handlerAdapter)。可裝配多個視圖解析器,配置優先級。

常用標簽理解

<mvc:annotation-driven/>

  • 默認創建並注冊DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。如果配置自定義的,則替換默認的。
  • 默認注冊FormattingConversionServiceFactoryBean(一個默認的ConversionService)。自定義conversionService使用屬性conversion-service來組裝。
  • 默認裝配LocalValidatorFactoryBean,支持controller方法的入參標注@Valid。

<mvc:default-servlet-handler />

  • 默認配置DefaultServletHttpRequestHandler,檢查URL,若是靜態資源,則將請求轉由web應用服務器默認的servlet處理。否則,由DispatcherServlet處理。
  • 如果web應用服務器默認servlet的名字不是“default”,則需要配置 <mvc:default-servlet-handler default-servlet-name="yourServerDefaultServletName"/>

<mvc:resources/>

  • 由Spring MVC框架自己處理靜態資源,並添加一些有用的附加功能。可將多個路徑映射為一個邏輯路徑;按照配置路徑順序查找,只要查找到,即返回。

其他

ExceptionHandler

標注在方法上,指定處理特定異常的方法。作用域:類。創建一個BaseController,里面指定各種異常的處理方法;其他controller繼承BaseController。

定義全局處理方法:配置SimpleMappingExceptionResolver 。

示例:

//處理一種異常@ExceptionHandler(RuntimeException.class)
//處理多種異常
 @ExceptionHandler({BindException.class,RuntimeException.class})
 @ResponseBody
 public Map<String, Object> bindExceptionHandler(BindException e, HttpServletResponse response, HttpServletRequest request) {
     HashMap body = new HashMap();
     body.put("status", Integer.valueOf(1));
     response.setStatus(200);
     return body;
 }

RequestBody

標注RequestBody,將參數按照屬性名匹配的方式,填充入POJO。支持級聯的屬性名。如下,為類結構圖,則傳遞user參數時,應該寫
userName=tom&dept.deptId=1&dept.address.tel=102

類結構圖

@RequestBody/ResponseBody

@RequestBody/ResponseBody是開發中常用的注解。

  • 數據類型轉換主要通過converter來實現,接口:HttpMessageConverter<T>,作用:將請求信息轉換為一個對象,將對象輸出為響應信息。相應接口:canRead,read,canWrite,write。相關標注:RequestBody,ResponseBody。
  • 當controller處理方法使用到@RequestBody/ResponseBody或者HttpEntity /ResponseEntity 時,才使用HttpMessageConverter對請求/響應消息進行處理。
  • 處理表單數據的FormHttpMessageConverter,處理的數據類型為MultiValueMap,所以如果要模擬post請求,需要將參數封裝成MultiValueMap,看這個示例

關於HttpMessageConverter的裝配和使用,看下源代碼:
RequestMappingHandlerAdapter 的屬性包含如下幾個:

	private HandlerMethodArgumentResolverComposite argumentResolvers;
	//參數解析器
	private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
	//請求報文和對象之間的轉換
	private List<HttpMessageConverter<?>> messageConverters;
	//封裝DataBinder,用於數據類型轉換、數據格式化、數據校驗
	private WebBindingInitializer webBindingInitializer;

其中HandlerMethodArgumentResolverComposite 是包含了一個HandlerMethodArgumentResolver的List,用於解析參數。 WebBindingInitializer接口 封裝了WebDataBinder(繼承自DataBinder),前面ConfigurableWebBindingInitializer是它的一個具體實現。 HttpMessageConverter就是我們配置的數據轉換器。

RequestMappingHandlerAdapter構造方法中會添加默認的幾個HttpMessageConverter:

	public RequestMappingHandlerAdapter() {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316

		this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(stringHttpMessageConverter);
		this.messageConverters.add(new SourceHttpMessageConverter<Source>());
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}

設置之后,將messageConverters添加到參數解析其中:

	@Override
	public void afterPropertiesSet() {
		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
    ......
	}


	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		......
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
		......
		return resolvers;
	}

可以看到,將messageConverters放入RequestResponseBodyMethodProcessor中。然后再在RequestResponseBodyMethodProcessor中使用具體的messageConverters轉換報文和對象,同時對數據進行校驗。

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		//使用MessageConverters讀取報文,並轉為對象
		Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
		//創建WebDataBinder,獲取對象參數
		String name = Conventions.getVariableNameForParameter(parameter);
		WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
		//參數校驗,結果存入 binder.getBindingResult()
		if (argument != null) {
			validate(binder, parameter);
		}
        //將參數處理結果 binder.getBindingResult()放入mavContainer
		mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

		return argument;
	}

這里,把HttpMessageConverter和WebDataBinder的順序搞清楚了。其實,既然HttpMessageConverter是用來解析報文為對象的,肯定是放在參數處理第一步的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM