spring-web HandlerMethodArgumentResolver 源码分析

HandlerMethodArgumentResolver 的使用处,解析参数

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

HandlerMethodArgumentResolver 在解析参数时使用,以下是使用处 InvocableHandlerMethod#getMethodArgumentValues 方法。

  1. 获得方法的参数。
  2. 遍历方法参数,将参数解析成对应的类型。
    1. 获得当前遍历的 MethodParameter 对象,给它设置 parameterNameDiscoverer。
    2. 从 providedArgs 中获得参数。如果获得到,则进入下一个参数的解析,默认情况 providedArgs 不会传参。
    3. 判断 resolvers 是否支持当前的参数解析。
    4. 执行参数解析,解析成功后进入下一个参数的解析。
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
		// 获得方法的参数
		MethodParameter[] parameters = getMethodParameters();
		// 无参,返回空数组
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		// 遍历方法参数,将参数解析成对应的类型
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			// 获得当前遍历的 MethodParameter 对象,给它设置 parameterNameDiscoverer
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			// 从 providedArgs 中获得参数。如果获得到,则进入下一个参数的解析,默认情况 providedArgs 不会传参。
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			// 判断 resolvers 是否支持当前的参数解析
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				// 执行参数解析,解析成功后进入下一个参数的解析
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}

类层次

img

HandlerMethodArgumentResolver 接口

org.springframework.web.method.support.HandlerMethodArgumentResolver

public interface HandlerMethodArgumentResolver {
	/**
	 * 是否支持解析当前参数
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * 解析当前参数
	 */
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

HandlerMethodArgumentResolverComposite – 复合体

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

  1. 实现了 HandlerMethodArgumentResolver 接口,复合的 HandlerMethodArgumentResolver 实现类。使用了组合模式,作为一个整体承载了 argumentResolvers。

  2. 获取 argumentResolver 时,如果没有缓存,则需要遍历 argumentResolvers 集合获取当前 methodParameter 适合的 argumentResolver。因此引入 argumentResolverCache,作为 MethodParameter 与 HandlerMethodArgumentResolver 的映射缓存,用于快速获取。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
	/**
	 * 存储 argumentResolvers 的集合
	 */
	private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();

	/**
	 * MethodParameter 与 HandlerMethodArgumentResolver 的映射,作为缓存用于快速获取
	 */
	private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
}

HandlerMethodArgumentResolverComposite#resolveArgument 方法 – 解析参数

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument

解析参数,应用了策略模式。

  1. 获取当前 methodParameter 适合的 argumentResolver。
  2. 使用 argumentResolver 解析参数。
	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		// 获取当前 methodParameter 适合的 argumentResolver
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		// 使用 argumentResolver 解析参数
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

HandlerMethodArgumentResolverComposite#getArgumentResolver 方法-- 获取当前 methodParameter 适合的 argumentResolver

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

获取当前 methodParameter 适合的 argumentResolver。

  1. 优先从 argumentResolverCache 缓存中,获取当前 methodParameter 适合的 argumentResolver。
  2. 缓存中没有,遍历 argumentResolvers 集合获取当前 methodParameter 适合的 argumentResolver,HandlerMethodArgumentResolver#supportsParameter 方法用于判断是否支持。支持的 argumentResolver 加入缓存。
	/**
	 * Find a registered {@link HandlerMethodArgumentResolver} that supports
	 * the given method parameter.
	 *
	 * 获取当前 methodParameter 适合的 argumentResolver
	 */
	@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		// 优先从 argumentResolverCache 缓存中,获取当前 methodParameter 适合的 argumentResolver
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		// 缓存中没有,遍历 argumentResolvers 集合获取当前 methodParameter 适合的 argumentResolver
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				// 是否支持
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
                    // 支持的 argumentResolver 加入缓存,break 跳出遍历
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

AbstractNamedValueMethodArgumentResolver

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver

基于名称获取值的 HandlerMethodArgumentResolver 抽象基类,提供根据参数名称解析的能力。

例如,@RequestParam(value = “username”) 注解,使用 AbstractNamedValueMethodArgumentResolver 从请求中获得 username 对应的参数值(更具体地说是其子类 RequestParamMethodArgumentResolver)。

子类主要有,RequestParamMethodArgumentResolver, RequestParamMapMethodArgumentResolver 和 PathVariableMethodArgumentResolver。

public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
	/**
	 * MethodParameter 和 NamedValueInfo 的映射,作为缓存
	 */
	private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);
}

NamedValueInfo

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.NamedValueInfo

NamedValueInfo 是静态内部类,对应注解的属性。

	/**
	 * 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;

		/**
		 * 默认值
		 */
		@Nullable
		private final String defaultValue;

		public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
			this.name = name;
			this.required = required;
			this.defaultValue = defaultValue;
		}
	}

AbstractNamedValueMethodArgumentResolver#resolveArgument 方法

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument

解析请求参数。

  1. 获得方法参数对应的 namedValueInfo 对象。
  2. 如果 parameter 是 Optional 类型,则获取内嵌的参数。否则使用 parameter 本身。
  3. 如果 name 是 spel 表达式(占位符),则解析成真实的 name。
  4. 解析 name 对应的值 arg,由子类实现。如果 arg 为空,则使用默认值。默认值不存在则处理参数缺省和空值的情况。
  5. 数据绑定相关。
  6. 后置处理解析的参数值。
	@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		// 获得方法参数对应的 namedValueInfo 对象
		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		// 如果 parameter 是 Optional 类型,则获取内嵌的参数。否则使用 parameter 本身
		MethodParameter nestedParameter = parameter.nestedIfOptional();

		// 如果 name 是 spel 表达式(占位符),则解析成真实的 name
		Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
		if (resolvedName == null) {
			throw new IllegalArgumentException(
					"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
		}

		// 解析 name 对应的值,由子类实现
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		// 如果 arg 为空,则使用默认值。默认值不存在则处理参数缺省和空值的情况。
		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 {
				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());
			}
		}

		// 后置处理解析的参数值
		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

		return arg;
	}

AbstractNamedValueMethodArgumentResolver#getNamedValueInfo 方法 – 获取 namedValueInfo

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#getNamedValueInfo

获取 namedValueInfo,先尝试从缓存中获取,没有则创建并加入缓存。

	/**
	 * Obtain the named value for the given method parameter.
	 */
	private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
		// 从 namedValueInfoCache 缓存中,获得 namedValueInfo 对象
		NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
		if (namedValueInfo == null) {
			// 获得不到 namedValueInfo 则创建,这是一个抽象方法,由子类具体实现
			namedValueInfo = createNamedValueInfo(parameter);
			// 更新 namedValueInfo 对象
			namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
			// 添加到 namedValueInfoCache 缓存中
			this.namedValueInfoCache.put(parameter, namedValueInfo);
		}
		return namedValueInfo;
	}

RequestParamMethodArgumentResolver

处理普通的请求参数。

public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver implements UriComponentsContributor {
}

RequestParamMethodArgumentResolver#createNamedValueInfo 方法 – 创建方法参数对应的 namedValueInfo 对象

org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#createNamedValueInfo

  1. 如果方法参数有 @RequestParam 注解,则根据注解创建一个 RequestParamNamedValueInfo 对象,获取注解中的 name, required 和 defaultValue 配置。
  2. 否则,就创建一个空的 RequestParamNamedValueInfo 对象,三个属性分别为,空字符串"", false 和 ValueConstants.DEFAULT_NONE。
	@Override
	protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
		RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
		return (ann != null ? new RequestParamNamedValueInfo(ann) : new 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());
		}
	}

RequestParamMethodArgumentResolver#supportsParameter – 是否支持解析当前参数

org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#supportsParameter

判断此 argumentResolver 是否支持解析当前参数。

  1. 有 @RequestParam 注解的情况。
    1. 如果是 Map 类型,则 @RequestParam 注解必须要有 name 属性。
    2. 有 @RequestParam 注解的情况且非 Map,则直接适用。
  2. 没有 @RequestParam 注解的情况。
    1. 如果有 @RequestPart 注解,返回 false。
    2. 获得参数,内嵌(Optional)参数则获得内嵌值,非内嵌参数直接用 parameter 本身。
    3. 如果是 Multipart 参数,则适用。如果开启 useDefaultResolution 功能,则判断是否为普通类型。
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		// 有 @RequestParam 注解的情况
		if (parameter.hasParameterAnnotation(RequestParam.class)) {
			// 如果是 Map 类型,则 @RequestParam 注解必须要有 name 属性
			if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
				RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
				return (requestParam != null && StringUtils.hasText(requestParam.name()));
			}
			// 有 @RequestParam 注解的情况且非 Map,则直接适用
			else {
				return true;
			}
		}
		// 没有 @RequestParam 注解的情况
		else {
			// 如果有 @RequestPart 注解,返回 false
			if (parameter.hasParameterAnnotation(RequestPart.class)) {
				return false;
			}
			// 获得参数,内嵌(Optional)参数则获得内嵌值,非内嵌参数直接用 parameter 本身
			parameter = parameter.nestedIfOptional();
			// 如果是 Multipart 参数,则适用
			if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
				return true;
			}
			// 如果开启 useDefaultResolution 功能,则判断是否为普通类型
			else if (this.useDefaultResolution) {
				return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
			}
			else {
				return false;
			}
		}
	}

RequestParamMethodArgumentResolver#resolveName

org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName

解析参数 name 对应的值。

  1. 情况1,HttpServletRequest 情况下的 MultipartFile 和 Part 的情况。
  2. 情况2,MultipartHttpServletRequest 情况下的 MultipartFile 的情况。
  3. 情况3,普通参数的获取。例如常见的 String, Integer 之类的请求参数,直接从请求中获取参数值。

情况1和2,处理参数类型为文件(org.springframework.web.multipart.MultipartFile) 和 javax.servlet.http.Part 参数的获取。

	@Override
	@Nullable
	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
		// 情况一,HttpServletRequest 情况下的 MultipartFile 和 Part
		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 情况下的 MultipartFile 的情况
		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;
	}

RequestParamMapMethodArgumentResolver

org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver

实现了 HandlerMethodArgumentResolver 接口,用于处理带有 @RequestParam 注解,但是注解上没有 name 属性的 Map 类型的参数。

public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
}

RequestParamMapMethodArgumentResolver 和 RequestParamMethodArgumentResolver 对比

RequestParamMapMethodArgumentResolver 的机制是,将所有参数添加到 Map 集合中

@RequestMapping("/hello")
public String hello4(@RequestParam Map<String, Object> map) {
    return "666";
}

发送请求 GET /hello?name=yyy&age=20,name 和 age 参数,就会都添加到 map 中。

RequestParamMethodArgumentResolver 的机制是,将指定名字的参数添加到 Map 集合中。

@RequestMapping("/hello")
public String hello5(@RequestParam(name = "map") Map<String, Object> map) {
    return "666";
}

发送请求 GET /hello4?map={“name”: “yyyy”, age: 20}, map 参数的元素都会添加到方法参数 map 中。当然,要注意下,实际请求要 UrlEncode 编码下参数,实际请求是 GET /hello?map=%7b%22name%22%3a+%22yyyy%22%2c+age%3a+20%7d

PathVariableMethodArgumentResolver

org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver

实现 UriComponentsContributor 接口,继承 AbstractNamedValueMethodArgumentResolver 抽象类,从请求路径中解析参数。

public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver implements UriComponentsContributor {
}

supportsParameter

org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#supportsParameter

判断此 argumentResolver 是否支持解析当前参数。

	/**
	 * 判断此 argumentResolver 是否支持解析当前参数
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		// 必须有 @PathVariable 注解才能支持
		if (!parameter.hasParameterAnnotation(PathVariable.class)) {
			return false;
		}
		// 请求参数如果是 Map 类型,必须有 @PathVariable 注解且有 name 属性才能支持
		if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
			PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
			return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
		}
		return true;
	}

resolveName

org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName

实现 resolveName(String name, MethodParameter parameter, NativeWebRequest request) 方法,从请求路径中获取方法参数的值。

	@Override
	@SuppressWarnings("unchecked")
	@Nullable
	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);
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
dynamic-datasource-spring-boot-starter 是一个基于 Spring Boot 的动态数据源库。它提供了在运行时动态切换数据源、动态创建数据源以及多租户的支持。 对于 dynamic-datasource-spring-boot-starter 的源码分析,可以从以下几个方面进行思考: 1. 数据源的自动装配:源码中可能会使用 Spring Boot 的自动配置功能,通过扫描配置类或者注解的方式,自动将数据源相关的类和 bean 注入到应用程序中。 2. 动态切换数据源的实现:动态切换数据源是 dynamic-datasource-spring-boot-starter 的核心功能之一。源码中可能会涉及到 AOP、动态代理等技术,通过拦截器或者代理的方式,在运行时根据特定条件切换数据源。 3. 动态创建数据源的实现:动态-datasource-spring-boot-starter 提供了在运行时动态创建数据源的功能。源码中可能会包含一些工厂类或者构建者模式的实现,根据配置信息动态创建数据源实例。 4. 多租户支持的实现:多租户是指一个系统可以同时服务于多个不同的客户或租户。dynamic-datasource-spring-boot-starter 也提供了对多租户的支持。源码中可能会包含一些多租户相关的类和逻辑,如解析请求或者从配置文件中获取租户信息等。 总结来说,dynamic-datasource-spring-boot-starter 源码分析涉及到数据源的自动装配、动态切换数据源的实现、动态创建数据源和多租户支持等方面的内容。可以通过阅读源码来深入了解其实现原理和技术细节,从而更好地使用和定制该库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JellyfishMIX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值