spring-web HandlerMethodArgumentResolver 源码分析

news2024/10/2 8:33:18

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);
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/352593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试题----集合

概述 从上图可以看出&#xff0c;在Java 中除了以 Map 结尾的类之外&#xff0c; 其他类都实现了 Collection 接⼝。 并且&#xff0c;以 Map 结尾的类都实现了 Map 接⼝List,Set,Map List (对付顺序的好帮⼿)&#xff1a; 存储的元素是有序的、可重复的。 Set (注重独⼀⽆⼆…

2.Telegraf简介

Telegraf简介 Telegraf是Influx公司一款基于插件化的开源指标收集工具.主要结合时序性数据库进行使用,用于性能监控.通常Telegraf会每间隔一段时间抓取一批指标数据并将数据发送给时序性数据库或其他自定义的Output. 官方文档 https://docs.influxdata.com/telegraf/v1.24 与…

分享7个比B站更刺激的老司机网站,别轻易点开

俗话说摸鱼一时爽&#xff0c;一直摸一直爽&#xff0c;作为一个程序员老司机了&#xff0c;一头乌黑浓密的头发还时不时被同事调侃&#xff0c;就靠这10个网站让我健康生活&#xff0c;不建议经常性使用&#xff0c;因为还有一句俗话&#xff0c;那就是“摸鱼一时爽&#xff0…

22- 隐马尔科夫HMM (NLP自然语言算法) (算法)

HMM模型 : from hmmlearn.hmm import GaussianHMM model GaussianHMM(n_components3,n_iter100000, covariance_type diag) model.fit(X) 1、马尔科夫链 有向图模型&#xff08;贝叶斯网络&#xff09;&#xff1a;用有向图表示变量间的依赖关系&#xff1b; 无向图模型&…

【GlobalMapper精品教程】051:融合Dissolve操作详解

本节讲解globalmapper中融合Dissolve工具的使用。 文章目录 一、工具介绍1. 工具位置2. 融合工具二、案例实战1. 加载实验数据2. 根据字段分组融合案例一:根据地类名称分组,将相同的类型融合到一起。案例二:根据权属地类名称分组,将相同的类型融合到一起。一、工具介绍 1.…

UE4 手把手教你做插件(1) 从代码引用插件

0&#xff0c;前言 我看的是 技术宅阿棍儿 的视频&#xff0c;B站有。 系列视频&#xff1a;从代码引用插件_哔哩哔哩_bilibili 看不懂&#xff0c;只能边查资料边看&#xff0c;讲的顺序有点乱 1&#xff0c;根据视频提示创建第三方插件 注意&#xff1a;如果只有空白插件的情…

如何从手工测试进阶自动化测试?阿里10年测开经验分享...

随着行业的竞争加剧&#xff0c;互联网产品迭代速度越来越快&#xff0c;QA 与测试工程师都需要在越来越短的测试周期内充分保证质量。可是&#xff0c;App 测试面临着很多挑战&#xff0c;比如多端发布、多版本发布、多机型发布等等&#xff0c;导致了手工测试很难完全胜任。因…

使用yolov5和强化学习训练一个AI智能欢乐斗地主(一)

这里写自定义目录标题项目介绍项目过程介绍训练yolov5目标检测斗地主收集数据集yolov5调参项目介绍 你好&#xff01; 欢迎阅读我的文章&#xff0c;本章将介绍&#xff0c;如何使用yolov5和强化学习训练一个AI斗地主&#xff0c;本项目将分为三个部分&#xff0c;其中包含&am…

一篇搞懂Mock测试

1. 什么是Mock测试 mock测试就是在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象/数据/场景&#xff0c;用一个虚拟的对象来创建以便测试的测试方法。 2. Mock测试常见场景 无法控制第三方系统接口的返回&#xff0c;返回的数据不满足要求依赖的接口还未开…

DEM数据下载——以地理空间数据云为例

数字高程模型&#xff08;Digital Elevation Model)是进行地形分析的重要基础&#xff0c;诸如坡度、坡向及水文分析等都在此基础上进行。今天&#xff0c;我们一起来聊一聊一种DEM数据常见下载方式。按照惯例&#xff0c;先将网址列出&#xff1a;https://www.gscloud.cn/home…

2023年美赛 MCM B题 重新构想马赛马拉岛

背景肯尼亚的野生动物保护区最初主要是为了保护野生动物和其他自然资源。肯尼亚议会于2013 年通过了《野生动物保护和管理法》&#xff0c;以提供更公平的资源共享&#xff0c;并允许进行替代的、以社 区为基础的管理工作[1].此后&#xff0c;肯尼亚增加了修正案&#xff0c;以…

有特别有创意的网站设计案例

有人说 UI 设计师集艺术性与科学性于一身&#xff0c;不仅需要对工具的使用熟练&#xff0c;更需要对美术艺术有一定的基础了解。如果想要成为优秀的 UI 设计师是一个需要磨砺的过程&#xff0c;需要不断的学习和积累&#xff0c;多看多练多感受&#xff0c;其中对于优质的设计…

力扣53.最大子数组和

文章目录力扣53.最大子数组和题目描述贪心动态规划力扣53.最大子数组和 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例…

4、HAL库SPI数据收发

1、通过SPI轮询的方式实现数据收发 1.1、通过CubeMx配置SPI轮询的方式实现数据收发 SPI模式 CPOL CPHA 空闲时 SCK 时钟 采样时刻 0 0 0 低电平 奇数边沿(W25Qxx支持) 1 0 1 低电平 偶数边沿 2 1 0 高电平 奇数边沿 3 1 1 高电平 偶数边沿(W25Qxx支持)当前使用 生成代码工程 …

[计算机操作系统(慕课版)]第一章 操作系统引论(学习笔记)

操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是配置在计算机硬件上的第一层软件&#xff0c;是对硬件系统的首次扩充。操作系统的主要作用&#xff1a; 管理硬件设备&#xff0c;提高他们的利用率和系统吞吐量 利用率&#xff1a;使硬件充分忙碌系统吞吐量…

Python|每日一练|树|深度优先搜索|数组|二分查找|链表|双指针|单选记录:填充每个节点的下一个右侧节点指针|搜索插入位置|旋转链表

1、填充每个节点的下一个右侧节点指针&#xff08;树&#xff0c;深度优先搜索&#xff09; 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node { int val; Node *left; Node *rig…

同一个整型常量怎样在不同进制间之间转换?

整型常量可以分别用二进制、八进制、十进制和十六进制表示&#xff0c;不同的进制并不影响数据本身的大小&#xff0c;同一个整型常量可以在不同进制之间转换&#xff0c;具体转换方式如下。1.十进制和二进制之间的转换(1)十进制转二进制。十进制转换成二进制就是一个除以2取余…

INDEMIND:2023,服务机器人能否“狂飙”?

过去一年&#xff0c;服务机器人的发展并不顺利。头部厂商的变动&#xff0c;市场增长率的持续下降不由得让人质疑&#xff0c;2023年&#xff0c;服务机器人还能“狂飙”吗&#xff1f;短期的市场波动&#xff0c;并不能代表行业的未来走向&#xff0c;而行业的两面矛盾现象&a…

珞珈1号-数据预处理流程

珞珈1号-数据预处理流程 1、重投影Albers 2、重采样 3、辐射校正–将INT32转化为浮点型真实数据 4、统一量纲(eg:和NPP同一量纲) 5、去噪 1、重投影Albers 参考这篇文章 2、重采样 输入想要重采样的数据&#xff0c;其中X和Y是你想要的大小&#xff0c;125即是重采样至125m …

kaggle竞赛-宠物受欢迎程度(赛题讲解与数据分析)

比赛官网地址 赛题介绍 petfinder是马来西亚领先的动物福利平台宠物网站地址 该网站使用可爱指数来排名宠物照片。它分析了图片组成和其他因素&#xff0c;并与数千个宠物档案的表现进行了比较。 在这场比赛中&#xff0c;你将分析原始图像和元数据来预测宠物照片的“Pawp…