??Spring MVC中使用HandlerMethodArgumentResolver策略介面來定義處理器方法引數決議器,@RequestParam使用的是RequestParamMapMethodArgumentResolver和RequestParamMethodArgumentResolver,接下來一起來深入了解一下其原始碼實作,
??類結構


??類決議
??HandlerMethodArgumentResolver和AbstractNamedValueMethodArgumentResolver是決議策略的上層定義和抽象,關于這兩個類可以參照《Spring 注解面面通 之 @CookieValue引數系結原始碼決議》中的決議,
??RequestParamMapMethodArgumentResolver和RequestParamMethodArgumentResolver則是用來針對不用型別的方法引數的決議,
??1) RequestParamMapMethodArgumentResolver實作了HandlerMethodArgumentResolver的supportsParameter(...)和resolveArgument(...)方法,
??RequestParamMapMethodArgumentResolver相對比較簡單,但在某些條件成立的情況下才會使用此類進行決議:
??① 方法引數由@RequestParam注解注釋,
??② 方法引數型別必須是Map型別,
??③ @RequestParam注解的name不能有值,
??resolveArgument(...)在決議引數時,從NativeWebRequest(HttpServletRequest的包裝)中獲取所有引數,針對MultiValueMap和普通Map兩種引數型別進行處理:
??① 引數型別為MultiValueMap時,回傳LinkedMultiValueMap實體,包含所有請求引數,
??② 引數型別為Map時,回傳LinkedHashMap實體,包含所有請求引數,
package org.springframework.web.method.annotation;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;
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.ModelAndViewContainer;
/**
* 決議用@RequestParam注釋的Map型別方法引數,其中未指定請求引數名稱.
*
* 創建的Map包含所有請求引數名稱/值對.
* 如果方法引數型別是MultiValueMap,那么對于請求引數具有多個值的情況,
* 創建的映射包含所有請求引數和值.
*/
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 方法引數檢查.
* 方法引數由@RequestParam注釋,且@RequestParam的name屬性為空.
* 方法引數型別必須為Map型別.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));
}
/**
* 決議方法引數值.
*/
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
// 獲取請求的所有引數.
Map<String, String[]> parameterMap = webRequest.getParameterMap();
// 方法引數型別為MultiValueMap.
if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
for (String value : values) {
result.add(key, value);
}
});
return result;
}
// 方法引數型別為非MultiValueMap的Map型別.
else {
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
result.put(key, values[0]);
}
});
return result;
}
}
}
??2) RequestParamMethodArgumentResolver繼承自抽象AbstractNamedValueMethodArgumentResolver(可以參照《Spring 注解面面通 之 @CookieValue引數系結原始碼決議》),
??RequestParamMethodArgumentResolver除了能處理@RequestParam注解外,還可以處理@RequestPart注解:
??當處理@RequestParam注解時,需在某些條件成立的情況下才會使用此類進行決議:
??① 方法引數由@RequestParam注解注釋,
??② 方法引數若是Map型別時,@RequestParam的name屬性不能為空,
??③ 方法引數若不是Map型別時,都可以處理,
??當處理@RequestPart注解時,需在某些條件成立的情況下才會使用此類進行決議:
??① 方法引數不可由@RequestPart注解注釋,
??② 方法引數型別為org.springframework.web.multipart.MultipartFile、org.springframework.web.multipart.MultipartFile集合、org.springframework.web.multipart.MultipartFile陣列、javax.servlet.http.Part、javax.servlet.http.Part集合或javax.servlet.http.Part陣列,
???③ 一個簡單型別的方法引數,包括:boolean、byte、char、short、int、long、float、double、Enum.class、CharSequence.class、Number.class、Date.class、URI.class、URL.class、Locale.class或Class.class,
package org.springframework.web.method.annotation;
import java.beans.PropertyEditor;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.UriComponentsContributor;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* 決議用@RequestParam注釋的方法引數,MultipartFile型別的引數與
* Spring的{@link MultipartResolver}抽象結合使用,以及AAA型別的引數與Servlet 3.0多部分請求一起使用.
* 這個決議器也可以在默認的決議模式下創建,在這種模式下,沒有用RequestParam注釋的簡單型別(int、long等)也被視為請求引數,引數名從引數名派生.
*
* 如果方法引數型別是Map,則使用注釋中指定的名稱來決議請求引數字串值.
* 然后通過型別轉換將該值轉換為Map,假設已經注冊了合適的Converter或PropertyEditor.
* 或者,如果沒有指定請求引數名,則使用RequestParamMapMethodArgumentResolver以映射的形式提供對所有請求引數的訪問.
*
* 呼叫@WebDataBinder將型別轉換應用于尚未與方法引數型別匹配的已決議請求頭值.
*/
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
/**
* String 型別描述符.
*/
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
private final boolean useDefaultResolution;
/**
* @param useDefaultResolution 在默認決議模式下,一個簡單型別的方法引數,
* 如BeanUtils.isSimpleProperty中定義的那樣,被視為一個請求引數,
* 即使它沒有被注釋,請求引數名是從方法引數名派生的.
*/
public RequestParamMethodArgumentResolver(boolean useDefaultResolution) {
this.useDefaultResolution = useDefaultResolution;
}
/**
* @param beanFactory 一個Bean工廠,用于決議默認值中的${…}占位符和#{…}SpEL運算式,如果默認值不包含運算式,則為null.
* @param useDefaultResolution 在默認決議模式下,一個簡單型別的方法引數,
* 如BeanUtils.isSimpleProperty中定義的那樣,被視為一個請求引數,
* 即使它沒有被注釋,請求引數名是從方法引數名派生的.
*/
public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory,
boolean useDefaultResolution) {
super(beanFactory);
this.useDefaultResolution = useDefaultResolution;
}
/**
* 方法引數檢查:
* 方法引數由@RequestParam注釋,且@RequestParam的name屬性為空.
* 方法引數型別為Map型別.
* 方法引數不可由@RequestPart注釋.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 處理@RequestParam注解.
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
return true;
}
}
// 處理@RequestPart注解.
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
/**
* 創建NamedValueInfo.
*/
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
/**
* 決議方法引數值.
*/
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
// 決議多部分請求值.
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartHttpServletRequest multipartRequest = request.getNativeRequest(MultipartHttpServletRequest.class);
// 決議多部分請求值.
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
// 決議普通引數值.
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
/**
* 處理引數缺失例外.
*/
@Override
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)
throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
if (servletRequest == null || !MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
throw new MultipartException("Current request is not a multipart request");
}
else {
throw new MissingServletRequestPartException(name);
}
}
else {
throw new MissingServletRequestParameterException(name,
parameter.getNestedParameterType().getSimpleName());
}
}
@Override
public void contributeMethodArgument(MethodParameter parameter, @Nullable Object value,
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
Class<?> paramType = parameter.getNestedParameterType();
if (Map.class.isAssignableFrom(paramType) || MultipartFile.class == paramType || Part.class == paramType) {
return;
}
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ?
parameter.getParameterName() : requestParam.name());
Assert.state(name != null, "Unresolvable parameter name");
if (value == null) {
if (requestParam != null &&
(!requestParam.required() || !requestParam.defaultValue().equals(ValueConstants.DEFAULT_NONE))) {
return;
}
builder.queryParam(name);
}
else if (value instanceof Collection) {
for (Object element : (Collection<?>) value) {
element = formatUriValue(conversionService, TypeDescriptor.nested(parameter, 1), element);
builder.queryParam(name, element);
}
}
else {
builder.queryParam(name, formatUriValue(conversionService, new TypeDescriptor(parameter), value));
}
}
@Nullable
protected String formatUriValue(
@Nullable ConversionService cs, @Nullable TypeDescriptor sourceType, @Nullable Object value) {
if (value == null) {
return null;
}
else if (value instanceof String) {
return (String) value;
}
else if (cs != null) {
return (String) cs.convert(value, sourceType, STRING_TYPE_DESCRIPTOR);
}
else {
return value.toString();
}
}
/**
* RequestParamNamedValueInfo.
*/
private static class RequestParamNamedValueInfo extends NamedValueInfo {
public RequestParamNamedValueInfo() {
super("", false, ValueConstants.DEFAULT_NONE);
}
public RequestParamNamedValueInfo(RequestParam annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
??總結
??@RequestParam是用來處理Web請求頭中的資訊,隨著網站的多樣和多元化,@RequestParam使用頻率會越來越廣泛,
??若文中存在錯誤和不足,歡迎指正!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/230742.html
標籤:其他
上一篇:js小案例-皇帝選妃(open()方法和close()方法應用)
下一篇:jquery 實作 簡單 購物車 商品數量 和 商品的總價 (bootstrap),實作checkbox全選全不選
