1.参数解析器介绍
参数解析器用于解析Handler方法参数,例如经常看到Handler方法签名中有HttpServletRequest、HttpServletResponse等对象,还有请求参数中有@RequestBody、@PathVariable等注解修饰的对象,这些对象都是经过参数解析器解析后注入的
SpringMVC中默认的参数解析器见RequestMappingHandlerAdapter#getDefaultArgumentResolvers
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// 基于注解的参数解析器
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// 基于类型的参数解析器
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// 添加自定义参数解析器
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// 能够处理所有场景
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
这些参数解析器会被封装到HandlerMethodArgumentResolverComposite中,处理参数时,会按照加入的顺序依次调用每个参数解析器执行解析任务,直到其中某个参数解析器能够解析此参数,则返回参数解析结果
2.自定义参数解析器
在某些场景下,我们可以自定义参数解析器,将某些数据通过参数传递到Handler方法中,例如在支付的场景中,我想要将渠道信息从请求头header中设置到Handler方法的参数中,这时我们可以自定义注解,然后再自定义参数解析器实现HandlerMethodArgumentResolver接口,将渠道的值传递到Handler方法中,示例如下:
1)创建Channel注解及自定义参数解析器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Channel {
}
public class ChannelArgumentResolver implements HandlerMethodArgumentResolver {
// 是否支持解析此参数
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
Channel channel = methodParameter.getParameterAnnotation(Channel.class);
return channel != null;
}
// 解析参数
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
String channel = nativeWebRequest.getHeader("x-chnl");
return channel;
}
}
2)在配置类中加入自定义参数解析器
这样初始HandlerAdapter时,就会将自定义的参数解析器加入到参数解析器集合中
@Configuration
@ComponentScan
public class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
// 添加自定义参数解析器
handlerAdapter.setCustomArgumentResolvers(Arrays.asList(new ChannelArgumentResolver()));
return handlerAdapter;
}
}
3)添加控制器
@RestController
public class Controller {
@GetMapping("/test")
public void test(@Channel String channel) {
System.out.println("渠道:" + channel);
}
}
4)添加启动类
public class Test {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
}
启动程序后使用postman调用http://127.0.0.1:8080/test,并在请求头中加入x-chnl=google,就能够打印出渠道信息,说明自定义参数解析器能够将渠道信息设置到test方法的参数中
3.参数解析器原理
参数解析器都实现了HandlerMethodArgumentResolver接口,它提供了如下两个方法
public interface HandlerMethodArgumentResolver {
// 是否支持解析此参数
boolean supportsParameter(MethodParameter parameter);
// 解析参数
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
接下来分析下常用的参数解析器的解析原理
3.1 RequestParamMethodArgumentResolver
3.1.1 RequestParamMethodArgumentResolver支持的参数类型
public boolean supportsParameter(MethodParameter parameter) {
// 情况一:如果参数上有@RequestParam注解
if (parameter.hasParameterAnnotation(RequestParam.class)) {
// 如果有@RequestParam注解,并且参数类型是Map的子类
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
// @RequestParam中的name属性如果有值,则支持
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
// 如果不是Map的子类则支持
else {
return true;
}
}
// 情况二:如果参数上没有@RequestParam注解
else {
// 如果参数上有@RequestPart,则不处理
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
// 如果参数类型是Multipart,则支持
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
// 构造时需要传入此参数,如果为true,则判断参数类型是不是简单类型
// 这里的简单类型指的是Enum、CharSequence、Number、Date、Temporal、URI、URL、Locale及Class
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
// 如果不满足上面所有的条件则不支持
else {
return false;
}
}
}
总结一下,RequestParamMethodArgumentResolver存在两种使用场景:
- 如果有@RequestParam注解,只要参数不是Map的子类且没有指定@RequestParam注解的name属性,其他情况都支持
- 如果没有@RequestParam注解,能支持解析Multipart类型,如果传入的useDefaultResolution为true的话,还能够支持解析一些简单类型
3.1.2 RequestParamMethodArgumentResolver解析流程
RequestParamMethodArgumentResolver继承自AbstractNamedValueMethodArgumentResolver,解析方法定义在父类中,源码见AbstractNamedValueMethodArgumentResolver#resolveArgument
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 1.获取参数名称和参数值信息
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 2.解析参数名称占位符及表达式
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 3.抽象方法子类实现,从请求中解析名称对应的数据
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
// 通过数据绑定工厂创建数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 4.使用数据绑定器转换成方法参数类型
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
// 5.抽象方法子类实现,处理解析后的值
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
AbstractNamedValueMethodArgumentResolver中解析参数分为如下5步:
1)获取参数名称和参数值信息
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
// 1.1 创建NamedValueInfo对象,抽象方法子类实现
namedValueInfo = createNamedValueInfo(parameter);
// 1.2 更新NamedValueInfo对象属性
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.isEmpty()) {
// 如果name为空,使用参数名称解析器解析参数名作为name
// 这就是为什么@RequestParm可以不写name属性的原因,它会自动匹配参数名称
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.");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
// MethodParameter#getParameterName
public String getParameterName() {
if (this.parameterIndex < 0) {
return null;
}
ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
if (discoverer != null) {
String[] parameterNames = null;
if (this.executable instanceof Method) {
// 使用参数解析器解析方法参数
parameterNames = discoverer.getParameterNames((Method) this.executable);
}
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;
}
// RequestParamMethodArgumentResolver#createNamedValueInfo
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
// 创建NamedValueInfo,其中name为@RequestParam注解中的name值,require是@RequestParam注解中的require值,defaultValue为默认值
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
// RequestParamMethodArgumentResolver.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注解中没有指定name属性,则会通过参数名称解析器解析方法参数名称作为name,参数名称解析器可参考SpringMVC源码分析(六)--参数名称解析器-CSDN博客
2)解析参数名称表达式
@RequestParam注解中name属性有可能设置为SpEL表达式,例如:@RequestParam(name="${test.name}"),
private Object resolveEmbeddedValuesAndExpressions(String value) {
if (this.configurableBeanFactory == null || this.expressionContext == null) {
return value;
}
// 解析占位符
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return value;
}
// 解析表达式
return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
}
3)从请求中解析名称对应的数据
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
// 解析multipart类型的请求参数
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
// 处理MultipartRequest类型的请求
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.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;
}
4)使用数据绑定器转换成方法参数类型
通过DataBinder将从请求中获取的数据转换为Handler方法的参数类型,这部分原理可参考SpringMVC源码分析(七)--数据绑定工厂-CSDN博客
5)处理解析后值
默认解析器中仅PathVariableMethodArgumentResolver重写了此方法
3.2 PathVariableMethodArgumentResolver
3.2.1 PathVariableMethodArgumentResolver支持的参数类型
public boolean supportsParameter(MethodParameter parameter) {
// 参数上必须有@PathVariable注解
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
// 如果参数类型是Map,@PathVariable注解中value必须有值
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}
从源码可知,PathVariableMethodArgumentResolver能解析带有@PathVariable注解的参数
3.2.2 PathVariableMethodArgumentResolver解析流程
PathVariableMethodArgumentResolver也继承自AbstractNamedValueMethodArgumentResolver,解析方法定义在父类中,参考上文RequestParamMethodArgumentResolver的解析流程,此处我们重点关注下PathVariableMethodArgumentResolver重写的方法
1)创建NamedValueInfo:从@PathVariable注解属性中获取
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
Assert.state(ann != null, "No PathVariable annotation");
return new PathVariableNamedValueInfo(ann);
}
private static class PathVariableNamedValueInfo extends NamedValueInfo {
public PathVariableNamedValueInfo(PathVariable annotation) {
super(annotation.name(), annotation.required(), ValueConstants.DEFAULT_NONE);
}
}
2)从请求中解析名称对应的数据
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
// 从请求中获取到路径占位符与实际值的映射关系
Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
// 获取路径上实际传入的值
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}
3)处理解析后值
protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer, NativeWebRequest request) {
String key = View.PATH_VARIABLES;
int scope = RequestAttributes.SCOPE_REQUEST;
Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
if (pathVars == null) {
pathVars = new HashMap<>();
// 向Request域中设置属性,即路径上占位符-转换类型后的实际值的映射关系
request.setAttribute(key, pathVars, scope);
}
pathVars.put(name, arg);
}
3.3 RequestResponseBodyMethodProcessor
3.3.1 RequestResponseBodyMethodProcessor支持的参数类型
public boolean supportsParameter(MethodParameter parameter) {
// 参数上有@RequestBody注解
return parameter.hasParameterAnnotation(RequestBody.class);
}
3.3.2 RequestResponseBodyMethodProcessor解析逻辑
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 使用消息转换器将请求body中json数据转换为对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
// 如果参数是Optional类型,则将对象封装成Optional对象
return adaptArgumentIfNecessary(arg, parameter);
}
protected Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter parameter) {
if (parameter.getParameterType() == Optional.class) {
if (arg == null || (arg instanceof Collection && ((Collection) arg).isEmpty()) ||
(arg instanceof Object[] && ((Object[]) arg).length == 0)) {
return Optional.empty();
}
else {
return Optional.of(arg);
}
}
return arg;
}
RequestResponseBodyMethodProcessor解析时将请求体中数据通过MessageConverter转成目标对象
3.4 ServletRequestMethodArgumentResolver
3.4.1 ServletRequestMethodArgumentResolver支持的参数类型
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);
}
A.isAssignableFrom(B)用于判断B类型是不是A类型或其子类型
因此,ServletRequestMethodArgumentResolver能支持解析的参数类型有WebRequest、ServletRequest、MultipartRequest、HttpSession、PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone及ZoneId
3.4.2 ServletRequestMethodArgumentResolver具体解析逻辑
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
if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
return resolveNativeRequest(webRequest, paramType);
}
// 获取HttpServletRequest,处理其他支持的类型
return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeRequest;
}
private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
// 从请求中获取HttpSession
if (HttpSession.class.isAssignableFrom(paramType)) {
HttpSession session = request.getSession();
if (session != null && !paramType.isInstance(session)) {
throw new IllegalStateException(
"Current session is not of type [" + paramType.getName() + "]: " + session);
}
return session;
}
// 从请求中获取PushBuilder
else if (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {
return PushBuilderDelegate.resolvePushBuilder(request, paramType);
}
// 从请求中获取InputStream
else if (InputStream.class.isAssignableFrom(paramType)) {
InputStream inputStream = request.getInputStream();
if (inputStream != null && !paramType.isInstance(inputStream)) {
throw new IllegalStateException(
"Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
}
return inputStream;
}
// 从请求中获取Reader
else if (Reader.class.isAssignableFrom(paramType)) {
Reader reader = request.getReader();
if (reader != null && !paramType.isInstance(reader)) {
throw new IllegalStateException(
"Request body reader is not of type [" + paramType.getName() + "]: " + reader);
}
return reader;
}
// 从请求中获取Principal
else if (Principal.class.isAssignableFrom(paramType)) {
Principal userPrincipal = request.getUserPrincipal();
if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
throw new IllegalStateException(
"Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
}
return userPrincipal;
}
// 从请求中获取HttpMethod
else if (HttpMethod.class == paramType) {
return HttpMethod.resolve(request.getMethod());
}
// 从请求中获取Locale
else if (Locale.class == paramType) {
return RequestContextUtils.getLocale(request);
}
// 从请求中获取TimeZone
else if (TimeZone.class == paramType) {
TimeZone timeZone = RequestContextUtils.getTimeZone(request);
return (timeZone != null ? timeZone : TimeZone.getDefault());
}
// 从请求中获取ZoneId
else if (ZoneId.class == paramType) {
TimeZone timeZone = RequestContextUtils.getTimeZone(request);
return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
}
throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
}
3.5 ServletResponseMethodArgumentResolver
3.5.1 ServletResponseMethodArgumentResolver能支持的参数类型
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (ServletResponse.class.isAssignableFrom(paramType) ||
OutputStream.class.isAssignableFrom(paramType) ||
Writer.class.isAssignableFrom(paramType));
}
从源码可知,ServletResponseMethodArgumentResolver能支持解析的参数类型有ServletResponse、OutputStream及Writer
3.5.2 ServletResponseMethodArgumentResolver具体解析逻辑
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
if (mavContainer != null) {
mavContainer.setRequestHandled(true);
}
Class<?> paramType = parameter.getParameterType();
// 处理ServletResponse及HttpServletResponse
if (ServletResponse.class.isAssignableFrom(paramType)) {
// 获取响应实现类
return resolveNativeResponse(webRequest, paramType);
}
// 获取ServletResponse,处理其他支持的类型
return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
}
private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeResponse = webRequest.getNativeResponse(requiredType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeResponse;
}
private Object resolveArgument(Class<?> paramType, ServletResponse response) throws IOException {
// 从响应中获取OutputStream
if (OutputStream.class.isAssignableFrom(paramType)) {
return response.getOutputStream();
}
// 从响应中获取Writer
else if (Writer.class.isAssignableFrom(paramType)) {
return response.getWriter();
}
throw new UnsupportedOperationException("Unknown parameter type: " + paramType);
}