@RequestParam 注解原理
@RequestParam 注解原理
注:SpringMVC 版本 5.2.15
介紹
@RequestParam 注解用于綁定請(qǐng)求參數(shù)。它的具體內(nèi)容如下:
// 該注解作用的方法形參 @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestParam {/*** 要綁定的參數(shù)名*/@AliasFor("name")String value() default "";/*** 要綁定的參數(shù)名*/@AliasFor("value")String name() default "";/*** 是否必須提供參數(shù)。默認(rèn)為 true* 當(dāng)為 true 時(shí),不提供參數(shù)將拋出異常*/boolean required() default true;/*** 沒(méi)有提供參數(shù)時(shí),以該值作為參數(shù)值。* 提供了參數(shù)將會(huì)使用提供的參數(shù)值* 設(shè)置了該值的話,會(huì)隱式的設(shè)置 required 為 false*/String defaultValue() default ValueConstants.DEFAULT_NONE; }接下來(lái)我們看下 SpringMVC 的源碼中是怎樣用 @RequestParam 注解的。具體為何調(diào)用了以下方法可以看我的另一篇文章。[SpringMVC 執(zhí)行流程解析]
源碼分析
AbstractNamedValueMethodArgumentResolver # resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 創(chuàng)建一個(gè) NamedValueInfo 對(duì)象NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);MethodParameter nestedParameter = parameter.nestedIfOptional();// 解析參數(shù)名Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);if (resolvedName == null) {throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");}// 獲取參數(shù)值Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);// 沒(méi)有獲取到參數(shù)值if (arg == null) {// 是否設(shè)置了 defaultValue if (namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}// required 屬性是否為 true,為 true 則會(huì)拋出異常else if (namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}// 未獲取到參數(shù)值// 如果參數(shù)類型是 boolean 類型的,則設(shè)置參數(shù)值為 false// 如果參數(shù)類型是其他基本數(shù)據(jù)類型(原生類型,非包裝類型),則拋出異常arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());}else if ("".equals(arg) && namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}...return arg; }這里創(chuàng)建了一個(gè) NamedValueInfo 對(duì)象,我們來(lái)看下這個(gè)類。
/*** Represents the information about a named value, including name, whether it's required and a default value.*/ protected static class NamedValueInfo {private final String name;private final boolean required;@Nullableprivate final String defaultValue;public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {this.name = name;this.required = required;this.defaultValue = defaultValue;} }看它的屬性,是不是和 @RequestParam 注解中的屬性一樣,它就是用來(lái)包裝 @RequestParam 注解中的屬性的。接下來(lái)我們看一下它的創(chuàng)建過(guò)程。
AbstractNamedValueMethodArgumentResolver # getNamedValueInfo
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {// 從緩存中獲取NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);if (namedValueInfo == null) {// 創(chuàng)建一個(gè) NamedValueInfo 對(duì)象namedValueInfo = createNamedValueInfo(parameter);// 基于上面的 NamedValueInfo 對(duì)象// 創(chuàng)建一個(gè)新的 NamedValueInfo 對(duì)象namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);// 加入緩存this.namedValueInfoCache.put(parameter, namedValueInfo);}return namedValueInfo; }該方法首先會(huì)嘗試從緩存中獲取 NamedValueInfo 對(duì)象,緩存中沒(méi)有的話就調(diào)用 createNamedValueInfo() 方法去創(chuàng)建一個(gè) NamedValueInfo 對(duì)象,然后基于剛才創(chuàng)建的對(duì)象再調(diào)用 updateNamedValueInfo() 方法創(chuàng)建一個(gè)新的 NamedValueInfo 對(duì)象,最后加入緩存中。
RequestParamMethodArgumentResolver # createNamedValueInfo
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {// 獲取參數(shù)上的 @RequestParam 注解RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);// 加了 @RequestParam 注解使用有參構(gòu)造器創(chuàng)建一個(gè) RequestParamNamedValueInfo 對(duì)象// 沒(méi)有加 @RequestParam 注解使用無(wú)參構(gòu)造器創(chuàng)建一個(gè) RequestParamNamedValueInfo 對(duì)象return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } public RequestParamNamedValueInfo(RequestParam annotation) {super(annotation.name(), annotation.required(), annotation.defaultValue()); } public RequestParamNamedValueInfo() {super("", false, ValueConstants.DEFAULT_NONE); }該方法中會(huì)去嘗試獲取參數(shù)中的 @RequestParam 注解,并將它包裝成 RequestParamNamedValueInfo 對(duì)象
AbstractNamedValueMethodArgumentResolver # updateNamedValueInfo
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {// 獲取參數(shù)名。// @RequestParam 注解中的 value 屬性值String name = info.name;// 沒(méi)有獲取到參數(shù)名// 沒(méi)有加 @RequestParam 注解或沒(méi)有設(shè)置 value 屬性值if (info.name.isEmpty()) {// 去獲取參數(shù)名name = parameter.getParameterName();if (name == null) {throw new IllegalArgumentException("Name for argument of type [" + parameter.getNestedParameterType().getName() +"] not specified, and parameter name information not found in class file either.");}}// 解決 @RequestParam 注解的 defaultValue 的值不能設(shè)置為 null 的問(wèn)題String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);return new NamedValueInfo(name, info.required, defaultValue); }該方法中首先會(huì)獲取 @RequestParam 注解中的 value 屬性值作為參數(shù)名,如果參數(shù)上沒(méi)有加 @RequestParam 注解或沒(méi)有設(shè)置 value 屬性值,那么會(huì)調(diào)用 getParameterName() 方法去獲取參數(shù)名。而且通過(guò) ValueConstants.DEFAULT_NONE 這個(gè)值解決了 @RequestParam 注解的 defaultValue 的值不能設(shè)置為 null 的問(wèn)題。
MethodParameter # getParameterName
public String getParameterName() {if (this.parameterIndex < 0) {return null;}// 參數(shù)名發(fā)現(xiàn)器ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;if (discoverer != null) {String[] parameterNames = null;// 非構(gòu)造方法if (this.executable instanceof Method) {// 獲取參數(shù)名parameterNames = discoverer.getParameterNames((Method) this.executable);}// 構(gòu)造方法else if (this.executable instanceof Constructor) {parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable);}if (parameterNames != null) {this.parameterName = parameterNames[this.parameterIndex];}this.parameterNameDiscoverer = null;}return this.parameterName; }該方法中會(huì)去判斷調(diào)用的方法是構(gòu)造方法還是非構(gòu)造方法,然后調(diào)用 getParameterNames() 方法去獲取參數(shù)名。
LocalVariableTableParameterNameDiscoverer # getParameterNames
public String[] getParameterNames(Method method) {// 獲取橋接方法的原始方法Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);// 獲取參數(shù)名return doGetParameterNames(originalMethod); }該方法首先會(huì)判斷 method 是否是一個(gè)橋接方法,如果是橋接方法則會(huì)去獲取它的原始方法。然后調(diào)用 doGetParameterNames() 方法。
LocalVariableTableParameterNameDiscoverer # doGetParameterNames
private String[] doGetParameterNames(Executable executable) {Class<?> declaringClass = executable.getDeclaringClass();// 先從緩存中獲取參數(shù)名,獲取不到調(diào)用 inspectClass() 方法Map<Executable, String[]> map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass);return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null); }該方法首先回從緩存中獲取參數(shù)名,獲取不到則調(diào)用 inspectClass() 方法去獲取。
LocalVariableTableParameterNameDiscoverer # inspectClass
private Map<Executable, String[]> inspectClass(Class<?> clazz) {// 加載字節(jié)碼文件InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));if (is == null) {...return NO_DEBUG_INFO_MAP;}try {// 通過(guò) ASM 框架技術(shù)從字節(jié)碼文件中獲取參數(shù)名ClassReader classReader = new ClassReader(is);Map<Executable, String[]> map = new ConcurrentHashMap<>(32);classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);return map;}...return NO_DEBUG_INFO_MAP; }該方法中會(huì)通過(guò) ASM 框架技術(shù)從字節(jié)碼文件中獲取參數(shù)名。
執(zhí)行完這些就已經(jīng)可以獲取到參數(shù)名了。
再回到最開(kāi)始的方法
AbstractNamedValueMethodArgumentResolver # resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 創(chuàng)建一個(gè) NamedValueInfo 對(duì)象NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);MethodParameter nestedParameter = parameter.nestedIfOptional();// 解析參數(shù)名Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);if (resolvedName == null) {throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");}// 獲取參數(shù)值Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);// 沒(méi)有獲取到參數(shù)值if (arg == null) {// 是否設(shè)置了 defaultValue if (namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}// required 屬性是否為 true,為 true 則會(huì)拋出異常else if (namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}// 未獲取到參數(shù)值// 如果參數(shù)類型是 boolean 類型的,則設(shè)置參數(shù)值為 false// 如果參數(shù)類型是其他基本數(shù)據(jù)類型(原生類型,非包裝類型),則拋出異常arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());}else if ("".equals(arg) && namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}...return arg; }獲取參數(shù)名后就通過(guò) request.getParameter() 方法去獲取參數(shù)值,然后對(duì) @RequestParam 中的屬性進(jìn)行一 一判斷,內(nèi)容比較簡(jiǎn)單,留給大家自己看了。
總結(jié)
總結(jié)
以上是生活随笔為你收集整理的@RequestParam 注解原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python3的3D开发-基于blend
- 下一篇: fastjson为什么默认是无序的