@RequestBody注解和@ResponseBody注解实现原理源码剖析+自定义消息转换器

@RequestBody 的底层原理 它只是一个标记注解,实际逻辑由框架处理。@Override组件功能标记需要从请求体中反序列化的参数控制参数解析逻辑实际解析的处理器实际进行数据转换的组件默认使用的 JSON 反序列化器也可以查看 Spring Framework 的 GitHub 源码: 🔗。 阅读详情

@RequestBody:将请求中的参数json转换为对象
@ResponseBody:将对象转换为json响应给浏览器
@RequestBody和@ResponseBody的解析都发生在SpringMVC处理请求的第四步HandlerAdapter执行Handler逻辑并返回ModelAndView,此处只贴主要流程代码,全流程代码及处理请求的其他步骤请参考上篇文章:SpringMVC请求流程处理源码剖析

第四部主要做了三件事情:

  1. 遍历handler形参,根据不同形参类型以及是否存在@RequestBody、@RequestParam等和注解获取不同的参数解析器,利用参数解析器在请求中获取实参
  2. 反射调用Handler业务方法,获取返回值
  3. 根据返回值类型及方法是否存在@ResponseBody获取不同的返回值处理器,利用返回值处理器封装ModelAndView

一、@RequestBody源码分析(完成第四步骤的第一个事情)

首先进入获取handler实参的方法InvocableHandlerMethod#getMethodArgumentValues

	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 获取当前handler方法的形参
		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		// 创建新数组,用于封装handler被调用时的实参(不同类型的形参获取方式不一样)
		Object[] args = new Object[parameters.length];
		// 遍历形参
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			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;
	}

进入resolveArgument方法,获取对应的参数解析器并获取实参

HandlerMethodArgumentResolverComposite.class
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		// 根据不同类型的参数获取不同的参数解析器。如:String的参数解析器AbstractNamedValueMethodArgumentResolver、Map的参数解析器MapMethodProcessor、带有@RequestBody注解的参数解析器RequestResponseBodyMethodProcessor等
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		// 利用对应的参数解析器解析形参获取实参
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

进入getArgumentResolver

HandlerMethodArgumentResolverComposite.class
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			// 遍历参数处理器集合根据参数获取合适的参数处理器(适配器模式)处理@RequestBody的参数处理器是RequestResponseBodyMethodProcessor
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

进入supportsParameter,判断参数中是否带有@RequestBody注解如果有则此参数用RequestResponseBodyMethodProcessor解析器解析。

RequestResponseBodyMethodProcessor.class
public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

拿到参数解析器后进入RequestResponseBodyMethodProcessor#resolveArgument

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		parameter = parameter.nestedIfOptional();
		// 使用消息转换器读取,将请求中的参数读取转换成对象
		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());
			}
		}

		return adaptArgumentIfNecessary(arg, parameter);
	}

进入AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		MediaType contentType;
		boolean noContentType = false;
		try {
			// 获取contentType application/json
			contentType = inputMessage.getHeaders().getContentType();
		}
		catch (InvalidMediaTypeException ex) {
			throw new HttpMediaTypeNotSupportedException(ex.getMessage());
		}
		if (contentType == null) {
			noContentType = true;
			contentType = MediaType.APPLICATION_OCTET_STREAM;
		}

		Class<?> contextClass = parameter.getContainingClass();
		// 获取参数类型
		Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
		if (targetClass == null) {
			ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
			targetClass = (Class<T>) resolvableType.resolve();
		}

		HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
		Object body = NO_VALUE;

		EmptyBodyCheckingHttpInputMessage message;
		try {
			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
			// 遍历消息转换器,可以自定义消息转换器只要注入到HandlerAdapter的messageConverters属性
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				// 过滤合适的消息转换器canRead的底层判断逻辑是(该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)
				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
					if (message.hasBody()) {
						HttpInputMessage msgToUse =
								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						// 使用消息转换器转换消息read(targetClass, msgToUse) targetClass:目标参数类型、msgToUse:包含json的输入流,red的底层方法是readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)方法用于将数据流转换为指定对象,可在自定义消息转换器重写
						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
						body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
					}
					else {
						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
					}
					break;
				}
			}
		}
		catch (IOException ex) {
			throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
		}

		if (body == NO_VALUE) {
			if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
					(noContentType && !message.hasBody())) {
				return null;
			}
			throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
		}

		MediaType selectedContentType = contentType;
		Object theBody = body;
		LogFormatUtils.traceDebug(logger, traceOn -> {
			String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
			return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
		});

		return body;
	}

二、@ResponseBody源码分析(完成第四步骤的第三个事情)

反射执行完handler方法拿到返回之后进入HandlerMethodReturnValueHandlerComposite#handleReturnValue方法处理返回结果值

HandlerMethodReturnValueHandlerComposite.class
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		// 根据不同结果类型获取不同的结果处理器如:String类型的结果处理器ViewNameMethodReturnValueHandler、ModelAndView类型的
		// 结果处理器ModelAndViewMethodReturnValueHandler、带有@ResponseBody是的结果处理
		//RequestResponseBodyMethodProcessor等
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		// 调用结果处理器完成handler返回值与ModelAndViewContainer的关系组装
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

进入RequestResponseBodyMethodProcessor#handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		// 使用消息转换器写入响应Response
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

进入AbstractMessageConverterMethodProcessor#writeWithMessageConverters

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		Object body;
		Class<?> valueType;
		Type targetType;

		if (value instanceof CharSequence) {
			// 返回值对象
			body = value.toString();
			valueType = String.class;
			targetType = String.class;
		}
		else {
			body = value;
			valueType = getReturnValueType(body, returnType);
			// 返回值类型
			targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
		}

		if (isResourceType(value, returnType)) {
			outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
			if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
					outputMessage.getServletResponse().getStatus() == 200) {
				Resource resource = (Resource) value;
				try {
					List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
					outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
					body = HttpRange.toResourceRegions(httpRanges, resource);
					valueType = body.getClass();
					targetType = RESOURCE_REGION_LIST_TYPE;
				}
				catch (IllegalArgumentException ex) {
					outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
					outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
				}
			}
		}

		MediaType selectedMediaType = null;
		// 获取ContentType
		MediaType contentType = outputMessage.getHeaders().getContentType();
		if (contentType != null && contentType.isConcrete()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found 'Content-Type:" + contentType + "' in response");
			}
			selectedMediaType = contentType;
		}
		else {
			HttpServletRequest request = inputMessage.getServletRequest();
			List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
			List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

			if (body != null && producibleTypes.isEmpty()) {
				throw new HttpMessageNotWritableException(
						"No converter found for return value of type: " + valueType);
			}
			List<MediaType> mediaTypesToUse = new ArrayList<>();
			for (MediaType requestedType : acceptableTypes) {
				for (MediaType producibleType : producibleTypes) {
					if (requestedType.isCompatibleWith(producibleType)) {
						mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
					}
				}
			}
			if (mediaTypesToUse.isEmpty()) {
				if (body != null) {
					throw new HttpMediaTypeNotAcceptableException(producibleTypes);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
				}
				return;
			}

			MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

			for (MediaType mediaType : mediaTypesToUse) {
				if (mediaType.isConcrete()) {
					selectedMediaType = mediaType;
					break;
				}
				else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
					selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
					break;
				}
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Using '" + selectedMediaType + "', given " +
						acceptableTypes + " and supported " + producibleTypes);
			}
		}

		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			// 遍历消息转换器
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
						(GenericHttpMessageConverter<?>) converter : null);
				// 过滤合适的消息转换器canWrite的底层判断逻辑是 (该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {

					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
							// 调用消息转换器的write方法底层调用的是writeInternal(T t, HttpOutputMessage outputMessage)
							//用于将对象转换为指定格式文本写入Response,outputMessage为响应输出流,可在自定义消息转换器重写
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}
					return;
				}
			}
		}

		if (body != null) {
			throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
		}
	}

当使用RequestResponseBodyMethodProcessor处理器处理结果值是步骤四返回的ModelAndView为null,当ModelAndView为null时不执行SpringMVC处理请求的步骤七视图渲染和请求转发过程,所以最终接口响应为消息转换器写入Response输出流中的文本。

三、自定义消息转换器

实现效果:
1.自定义消息转换器MyMessageConverter实现HttpMessageConverter接口专门处理User类型的参数
2.可以name#张三,age#20 转换为User作为参数
3.可以将User转换为 “获取到名称为张三,年龄20岁的人”

1.构建demo工程目录:
在这里插入图片描述

DemoController.class

@Controller
@RequestMapping("/demo")
public class DemoController  {

    @RequestMapping("/handle01")
	@ResponseBody
    public User handle01(@RequestBody User user) {
		System.out.println(user);
        return user;
    }
}

User.class

public class User {

	private String name;
	private  Integer age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "User{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}
}

2.自定义消息转换器,(该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)

/**
 * 自定义消息转换器
 * 注:该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数
 */
public class MyMessageConverter extends AbstractHttpMessageConverter<User> {


	public MyMessageConverter() {
		// 新建一个我们自定义的媒体类型application/xxx-lago
		super(new MediaType("application", "xxx-lago", Charset.forName("UTF-8")));
	}


	@Override
	protected boolean supports(Class<?> clazz) {
		//表明只处理User类型的参数。
		return User.class.isAssignableFrom(clazz);
	}

	//重写readlntenal 方法,处理请求的数据。
	@Override
	protected User readInternal(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
		String str = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
		// 解析参数
		String[] split = str.split(",");
		String name = split[0].split("#")[1];
		String age = split[1].split("#")[1];
		User user=new User();
		user.setName(name);
		user.setAge(Integer.parseInt(age));
		return user;
	}

	//重写writeInternal ,处理如何输出数据到response。
	@Override
	protected void writeInternal(User user, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
		String outStr="获取到名称为"+user.getName()+",年龄"+user.getAge()+"岁的人";
		outputMessage.getBody().write(outStr.getBytes());
	}
}

3.将自定义的消息转换器注入到HandlerAdapter中

在这里插入图片描述

工程全部构建完毕,下面进行测试
请求postman:
在这里插入图片描述
在这里插入图片描述

响应postman:
在这里插入图片描述
最后科普一下默认的转换器HttpMessageConverter有:

ByteArrayHttpMessageConverter:支持返回值类型为byte[],content-type为application/octet-stream,/

StringHttpMessageConverter:支持的返回值类型为String,
ResourceHttpMessageConverter:支持的返回值类型为Resource,content-type为 /

SourceHttpMessageConverter:支持的返回值类型为DomSource,SAXSource,Source,StreamSource,content-type为application/xml,text/xml,application/*+xml

MappingJacksonHttpMessageConverter:判断返回值能否被格式化成json,content-type为 application/json,application/*+json

AllEncompassingFormHttpMessageConverter:支持的返回值类型为MultiValueMap,content-type为application/x-www-form-urlencoded,multipart/form-data

HttpMessageConverter主要针对那些不会返回view视图的response。其中StringHttpMessageConverter有两个构造函数。当你没有给它指定字符集时,使用默认的ISO-8859-1,这便是造成乱码的一个原因,由于我们经常使用utf-8,所以可以在构造它时指定一下字符集。

阿里云学生机2核4G,30分钟搞定DNF台服联机(CentOS 7.6保姆级教程) 本文提供阿里云学生机2核4G搭建DNF台服联机的详细教程,涵盖服务器选购、网络配置、服务端部署及客户端联机设置。通过CentOS 7.6系统优化和端口配置,30分钟内即可完成搭建,适合学生和技术爱好者低成本实现游戏联机体验。 阅读详情

相关推荐

2023年全国赛C题《 电容电感测量装置》设计报告

参考下面网站的方案参考LCR测试仪,基本工作原理为给DUT加上正弦激励信号,然后测得该DUT两端的电压和流过DUT的电流,即可通过计算得到DUT的性质和参数。对于一个理想电容,电流相位应该超前电容两端电压90°。然而实际的电容存在损耗,可以等效为一个理想电容Cp​和一个理想电阻Rp​的并联,因此电流超前电压的相位将小于90°,这个角度差即为损耗角。假设DUT两端电压V˙Vcosωt,流过DUT的电流为I˙Isinωt−φ。

moerjie的博客 4808

@RequestBody的使用

提示:建议一定要看后面的@RequestBody的核心逻辑源码以及六个重要结论!本文前半部分的内容都是一些基 本知识常识,可选择性跳过。 声明:本文是基于SpringBoot,进行的演示说明。 基础知识介绍: @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);GET方式无请求体,所以使用@RequestBo...

JustryDeng 104万+

从FedAvg到实战:手把手教你用PyTorch复现联邦学习经典论文实验(MNIST/CIFAR-10)

本文详细介绍了如何使用PyTorch复现联邦学习经典算法FedAvg在MNIST和CIFAR-10数据集上的实验。从环境搭建、数据非IID划分到模型实现和参数聚合,手把手教你掌握联邦学习的核心技术与实战技巧,帮助开发者和研究人员快速实现隐私保护的分布式机器学习方案。

weixin_29053695的博客 252

@RequestBody 接收数组、List 参数、@Deprecated 标记废弃方法

目录 @RequestBody 概述 接收单个 String 参数 接收 字符串数组 参数 接收 List<Map<String,Object> 参数 接收整形数组 接收单个 POJO 对象 接收 POJO List 参数 @RequestBody 概述 1、@RequestBody 主要用来接收前端传递给后端的 json 格式的数据的(请求体中的数据的) 2、...

蚩尤后裔-汪茂雄 12万+

Vmware虚拟机一打开就蓝屏解决方法

Vmware虚拟机一打开就蓝屏解决方法 问题: 一打开Vmware虚拟机就蓝屏,如下图所示: 解决方法1: 启用和关闭windows功能(对话框)——>虚拟机平台(选项打对勾,默认没勾选)——>确定后重启——>再进应该就好了。 解决方法2: ①.查看电脑中的Hyper-V服务是否关闭 1.打开控制面板-程序-程序和功能 2.双击启用或关闭Windows功能 3.把Hyper-V前面的勾去掉 如果后期发下没有反应,或者电脑本身Hyper-V的服务没有打开,可以先打开在关闭,试

m0_55901666的博客 26万+

pi/4QPSK调制解调原理

文章目录1. π/4\pi/4π/4QPSK 信号的调制原理2. 匹配滤波器与成形滤波器 1. π/4\pi/4π/4QPSK 信号的调制原理 减小相位突变量的QPSK由偏置键控QPSK(OK-QPSK)和 π/4\pi/4π/4偏置的QPSK( π/4\pi/4π/4QPSK)。由于 π/4\pi/4π/4QPSK是用差分相位编码产生的(因此也叫做 π/4\pi/4π/4DQPSK),能有效地进行差分解调和鉴频器解调,在一些不易提取相干载波的场合很有用。 在常规的QPSK中,输入数据经过串/并变换后,分成

gemengxia的博客 2万+

spring mvc 静态资源权限访问的设置

一.案例现象 1.controller层 /** * @author liujianfu * @description controller中业务方法的集合参数获取,要将集合参数封装到一个pojo中才可以 * 参数属性名与请求参数名称一致,参数值会自动映射匹配 * @date 2021/1/10 22:14 * @param * @return */ @RequestMapping("/bike6") .

健康平安的活着的专栏 1275

C#操作MySQL从入门到精通(3)——新建数据库、新建表

我们要首先新建一个数据库,并且在数据库中新建一系列的表来管理数据库。

qq_34059233的博客 949

注解RequestBody详解

之前很多注解老是不太清楚作用并且容易搞混,这次一次性搞明白! 这是最近在使用postman做接口测试的时候发现自己的这个知识盲点。我们通常使用postman做测试的时候最常用的就是post与get请求,两者的区别主要在于一个将参数放在url中进行传递一个将参数放在request body中进行传递这里我们不做过多陈述。直接上干货! RequestBody注解的主要作用就是用于接收前...

weixin_40159122的博客 6万+

【unity实战】Unity游戏开发:如何用ScriptableObject与序列化多态实现可复用的模块化效果系统?

本文介绍了两种实现卡牌游戏效果系统的模块化设计方案。方案一使用ScriptableObject创建独立的效果资产,通过组合实现不同效果,优点是数据可复用、可视化编辑,缺点是同类不同参数效果需创建大量SO文件。方案二利用SerializeReference特性直接嵌套效果实例,避免SO文件过多,但需要插件支持编辑器多态显示。两种方案都将效果与卡牌解耦,提高了代码复用性和扩展性,解决了硬编码带来的重复、维护困难和扩展不便等问题。开发者可根据项目需求选择合适方案,或结合两者优势构建更灵活的系统。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 435

【Python基础】01 如何安装Python及集成环境

本文详细介绍了Python 3.13开发环境的完整搭建流程,涵盖三大操作系统(Windows/macOS/Linux)的Python安装方法、PyCharm专业IDE的配置使用,以及pip包管理工具的应用技巧。内容包括:Python 3.13各系统安装指南、PyCharm社区版与专业版的安装配置、环境验证方法、pip的基本命令与镜像源优化、虚拟环境管理,以及常见问题的解决方案。通过本教程,读者可获得一个高效完备的Python开发环境,为后续编程学习打下坚实基础。

nmdbbzcl的博客 1415

SpringMVC源码剖析(五) - 消息转换器HttpMessageConverter

原文链接:https://my.oschina.net/lichhao/blog/172562 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。 Http请求的抽象 还是回到请求-响应,也就是解析请求体,然后返回响应报文这个最基本的Http请求过程中来。我们知道,在servlet标准中,可以.

Markix的博客 486

c++中,部署Ethercat IGH + PREEMPT_RT 环境,跑DC模式,如何实现?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1355

SpringMVC源码剖析消息转换器HttpMessageConverter

概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。 #Http请求的抽象 还是回到请求-响应,也就是解析请求体,然后返回响应报文这个最基本的Http请求过程中来。我们知道,在servl...

任枫的博客 247

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

摘要 SpringMVC使用消息转换器实现请求报文和对象、对象和响应报文之间的自动转换 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。 Http请求的抽象

z69183787的专栏 3659

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Spring MVC 消息转换器

HttpMessageConverter 处理HTTP请求和响应的转换器。 public interface HttpMessageConverter<T> { // 对于该Media类型是否可读 boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); // 获

kkkkk777的博客 607

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

SpringMVC源码剖析-消息转换器HttpMessageConverter

摘要: SpringMVC使用消息转换器实现请求报文和对象、对象和响应报文之间的自动转换 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。 Http请求的抽象 还是回到

littlelittleguai的博客 258

转载开源中国(http://my.oschina.net/lichhao/blog/172562):SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

目录[-] 概述Http请求的抽象HttpInputMessageHttpOutputMessageHttpMessageConverterRequestResponseBodyMethodProcessor思考 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息

sunian_love2015的专栏 553

【Spring学习笔记-MVC-1.3】消息转换器HttpMessageConverter

作者:ssslinppp      参考链接:SpringMVC源码剖析(五)-消息转换器HttpMessageConverter: http://my.oschina.net/lichhao/blog/172562 概述在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spr...

weixin_33874713的博客 73

深入分析Spring MVC中RequestBody与ResponseBody

      在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换。在Spring MVC内部是如何做到的呢?先记住下面这张图,然后对里面的每个对象进行分析: HttpInputMessage HttpInputMessage是对一次Http请求报文的抽象。接口定义了getBody方法用于得到http请...

everyok的博客 1284

【SpringMVC源码】源码分析

(1)DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。(2)HanlerMapping是springmvc中完成url到controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller。

weixin_44823875的博客 1197
上一篇: SpringMVC初始化流程(事件监听机制、父子容器关系)
下一篇: Spring事务传播行为——转载
躺平程序猿
博客等级 码龄8年 4870粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

躺平程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值