Spring MVC : 原理解析 -- 控制器方法中的参数 ServletRequest 值从何而来 ?

本文深入探讨了SpringMVC框架中如何处理ServletRequest参数,揭示了其背后的原理。通过分析源代码,详细解释了ServletRequestMethodArgumentResolver如何确保控制器方法能够正确访问到当前请求对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道,Spring MVC应用中,当通过如下方式定义一个控制器方法时,参数列表中的ServletRequest request无论放到参数列表的哪个位置,在方法体内,它都正确地代表当前请求对象,你有没有想过,这背后的原理是什么呢 ?

@Controller
public class DemoController {


    @RequestMapping(value = "/")
    public String myControllerMethod(ServletRequest request) {
        // 这里可以访问方法参数 request,
		// 它也代表当前请求对象
		
		// ... 
    }
}

通过分析Spring MVC的源代码,概括地讲,在调用控制器方法之前,Spring MVC先从请求上下文中通过配置的各种HandlerMethodArgumentResolver解析得到了每个方法参数的值(这当然包含参数列表中的ServletRequest request),然后才调用控制器方法,这样一来,方法体内自然能通过参数request访问到正确的请求对象了。

关于Spring MVC完整的调用一个控制器方法的过程分析,可以参考我的另外一个系列文章:Spring MVC : 控制器方法处理请求的过程分析 - 0. 概述,本文对此不在展开分析。本文中,我们主要关注Spring MVC具体用来处理参数ServletRequest request的那个HandlerMethodArgumentResolver:ServletRequestMethodArgumentResolver

接下来,我们来看ServletRequestMethodArgumentResolver的源代码。

首先,作为一个HandlerMethodArgumentResolver,它声明支持哪些特征的参数的方法实现为 :

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) || // <=== 看这里,看这里,看这里
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
				Principal.class.isAssignableFrom(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				ZoneId.class == paramType);
	}

从此方法实现不难看出,如果一个参数的类型是ServletRequest的可赋值类型,它的解析工作就会由ServletRequestMethodArgumentResolver来负责。

当然,你可能也注意到了,ServletRequestMethodArgumentResolver在这里声明支持很多其他类型的参数,比如HttpSession之类。是的,这正是为什么我们要以ServletRequest作为例子来讲解的原因,因为通过这个例子,你可以举一反三地理解很多其他类型参数是如何被处理的。

我们继续来看ServletRequest类型的参数被ServletRequestMethodArgumentResolver解析的过程:

    // ServletRequestMethodArgumentResolver 代码片段    
    // webRequest 是 Spring MVC 在处理一个请求过程中代表当前请求的对象,
    // 在 Spring MVC 使用某个 HandlerMethodArgumentResolver 解析控制器方法的某个参数时,
    // 总是会将 webRequest 传递给该 HandlerMethodArgumentResolver。
    // mavContainer, webRequest , binderFactory 共同组成了解析指定参数 parameter
    // 的一个上下文环境。
	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Class<?> paramType = parameter.getParameterType();

		// WebRequest / NativeWebRequest / ServletWebRequest
		if (WebRequest.class.isAssignableFrom(paramType)) {
			if (!paramType.isInstance(webRequest)) {
				throw new IllegalStateException(
						"Current request is not of type [" + paramType.getName() + "]: " + webRequest);
			}
			return webRequest;
		}

		// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
       // 如果参数的类型为  ServletRequest 的可赋值类型时,委托方法 resolveNativeRequest 做具体解析
		if (ServletRequest.class.isAssignableFrom(paramType) 
				|| MultipartRequest.class.isAssignableFrom(paramType)) {
			return resolveNativeRequest(webRequest, paramType);
		}

		// HttpServletRequest required for all further argument types
		return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
	}

作为接口HandlerMethodArgumentResolver定义的方法参数解析函数,resolveArgument首先会被传递参数NativeWebRequest webRequest,它代表了当前请求对象。从该方法的实现逻辑可见,如果控制器方法参数的类型为ServletRequest的可赋值类型时,它会委托方法resolveNativeRequest做具体的参数值解析。我们继续来看 :

    // ServletRequestMethodArgumentResolver 代码片段    
 	private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
       // requiredType 就是开发人员在控制器方法中指定的request 的类型,在上面的例子中,它就是  ServletRequest,
       // 实际上,它也可以继承自 ServletRequest 的某个子类
		T nativeRequest = webRequest.getNativeRequest(requiredType);
		if (nativeRequest == null) {
			throw new IllegalStateException(
					"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
		}
		return nativeRequest;
	}

通过分析ServletRequestMethodArgumentResolver以上几个方法的源代码,控制器方法中的参数ServletRequest值从何而来,是不是就很明白了呢 ?

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值