SpringMVC源码:参数解析、方法调用与返回值处理
参考资料:
《SpringMVC源码解析系列》
《SpringMVC源码分析》
《Spring MVC源码》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
前文:
《SpringMVC源码:DispatcherServlet初始化流程》
《SpringMVC源码:HandlerMapping加载1》
《SpringMVC源码:HandlerMapping加载2》
《SpringMVC源码:getHandler、getHandlerAdapter过程》
前文我们已经介绍了,doDispatch方法过程中的handler查找、适配器查找以及拦截器preHandle方法的执行,本文我们会继续介绍方法的调用。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//检查是否为Multipart请求,如果是则HttpServletRequest转换为MultipartHttpServletRequestprocessedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);//1.根据request信息寻找对应的Handler//并返回mappedHeadler,它是一个执行链,包含拦截器和自定义controllermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {//没有找到Handler则通过response反馈错误信息noHandlerFound(processedRequest, response);return;}// 2.根据当前的handler寻找对应的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 当前handler处理last-modifiedString method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//3.mappedHandler调用拦截器的preHandler方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 4.HandlerAdapter调用自定义Controller中的方法并返回ModelAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());//5.视图解析,为request请求找到视图applyDefaultViewName(processedRequest, mv);//6.调用拦截器的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}
}
目录
一、handle
1、AbstractHandlerMethodAdapter#handle
2、RequestMappingHandlerAdapter#handleInternal
3、RequestMappingHandlerAdapter#invokeHandlerMethod
4、ServletInvocableHandlerMethod#invokeAndHandle
InvocableHandlerMethod#invokeForRequest
5、RequestMappingHandlerAdapter#getModelAndView
二、@RequestBody与@ResponseBody
1、接口转换
2、参数解析
2.1、查找解析器
2.2、解析过程
3、返回值处理
3.1、选择处理器
3.2、处理过程
补充
一、handle
1、AbstractHandlerMethodAdapter#handle
这里以AbstractHandlerMethodAdapter类为例,可以看到内部实际上是调用了handleInternal方法,而这个方法实际上是由子类RequestMappingHandlerAdapter实现的。(注意下这里的返回值为ModelAndView)
@Overridepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}
2、RequestMappingHandlerAdapter#handleInternal
该方法主要进行了对请求request的相关校验、解析、转换、执行相关的handler的invokeHandlerMethod 并返回对应的数据视图对象。
@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//如果设置supportMethods、requireSession属性需要对其进行校验//如果处理的请求方式不是supportMethods集合支持的抛出异常//或者requireSession为true但是request不包含session同样抛出异常checkRequest(request);// 针对线程不安全的session 提供两种方式 一种是同步 从而保证该会话的用户串行执行请求// 一种是非同步处理请求 不管同步与否最终调用核心方法invokeHandlerMethod 调用HandlerMethodif (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {//执行过程调用(参数解析 过程调用)mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all...mav = invokeHandlerMethod(request, response, handlerMethod);}//为响应资源设置对应的缓存时间,指定时间内可以直接使用本地缓存if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;}
3、RequestMappingHandlerAdapter#invokeHandlerMethod
这一步中将HandlerMethod被封装ServletInvocableHandlerMethod,包裹上方法执行需要的信息并调用invokeAndHandle方法执行,最后通过getModelAndView获取视图。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//先将请求响应对象包装成ServletWebRequestServletWebRequest webRequest = new ServletWebRequest(request, response);try {//获取该方法的所有使用@InitBinder注解修饰的方法//包装成WebDataBinderFactory 在handlerMethod执行前调用(包含类作用范围的和全局作用范围的)WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//获取该方法的所有使用@ModelAttribute注解修饰的方法 包装成ModelFactory(包含类作用范围的和全局作用范围的)ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);//将HandlerMethod方法包装成ServletInvocableHandlerMethod(该对象是InvocableHandlerMethod子类)//设置相关的组件 比如设置参数解析组件argumentResolvers和返回结果处理组件returnValueHandlers//绑定组件binderFactory和parameterNameDiscovererServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//创建数据模型和视图的容器对象 见名知义其包含我们所常见的数据和视图对象ModelAndViewContainer mavContainer = new ModelAndViewContainer();//对于重定向相关的参数保存需要依赖flashMap对象,如果一个请求是重定向请求 则input_flash_map保存重定向入参//如果一个请求需要进行重定向 则参数会存放到output_flash_map中mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));//调用@ModelAttribute注解修饰的方法将对应的属性存放到mavContainer 的model中modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);//异步请求处理AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();if (logger.isDebugEnabled()) {logger.debug("Found concurrent result value [" + result + "]");}invocableMethod = invocableMethod.wrapConcurrentResult(result);}//通过反射执行相关的handlerMethod 涉及参数解析,返回值的处理、//invocableMethod已经包含了进行参数解析和返回值处理的组件对象invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}//主要处理sessionAttribute 以及对model中的属性进行属性编辑器的转换处理@InitBinder//对于view对象进行逻辑视图到物理视图的转换以及重定向参数的设置return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}
4、ServletInvocableHandlerMethod#invokeAndHandle
首先调用invokeForRequest进行参数解析与方法调用,得到返回值后将其封装入mavContainer中。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 请求处理Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 返回值处理this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}
InvocableHandlerMethod#invokeForRequest
invokeForRequest方法首先解析参数,然后反射执行方法。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//调用getMethodArgumentValues进行参数解析Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);//调用doInvoke方法反射执行handler处理器Object returnValue = doInvoke(args);return returnValue;}
protected Object doInvoke(Object... args) throws Exception {ReflectionUtils.makeAccessible(getBridgedMethod());return getBridgedMethod().invoke(getBean(), args);}
5、RequestMappingHandlerAdapter#getModelAndView
从mavContainer取出结果包装成ModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);if (mavContainer.isRequestHandled()) {return null;}//从mavContainer中取出model ModelMap model = mavContainer.getModel();//根据取出的model创建视图对象ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}//处理redirect请求if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}return mav;}
二、@RequestBody与@ResponseBody
以下内容参考自《Spring MVC源码(三) ----- @RequestBody和@ResponseBody原理解析》
在上文中我们介绍了invokeAndHandle方法,其中的两个步骤分别为参数解析与返回值处理,这一节我们结合两个常用注解@RequestBody与@ResponseBody来进行这部分内容的讲解。
首先来看个样例,第一个requestBody请求,使用@RequestBody将HTTP请求体转换成String类型,第二个responseBody请求,将Map对象转换成json格式输出到HTTP响应中。
@RequestMapping("/requestBody")
public void requestBody(@RequestBody String body, Writer writer) throws IOException{writer.write(body);
}@RequestMapping(value="/responseBody", produces="application/json")
@ResponseBody
public Map<String, Object> responseBody(){Map<String, Object> retMap = new HashMap<>();retMap.put("param1", "abc");return retMap;
}
这两个注解都实现了HTTP报文信息同Controller方法中对象的转换,下面我们就来进行原理分析。
1、接口转换
SpringMVC中为了实现HTTP消息体和参数及返回值进行转换,所有转换类都实现了HttpMessageConverter接口,该接口定义了5个方法,用于将HTTP请求报文转换为java对象,以及将java对象转换为HTTP响应报文。
public interface HttpMessageConverter<T> {// 当前转换器是否能将HTTP报文转换为对象类型boolean canRead(Class<?> clazz, MediaType mediaType);// 当前转换器是否能将对象类型转换为HTTP报文boolean canWrite(Class<?> clazz, MediaType mediaType);// 转换器能支持的HTTP媒体类型List<MediaType> getSupportedMediaTypes();// 转换HTTP报文为特定类型T read(Class<? extends T> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;// 将特定类型对象转换为HTTP报文void write(T t, MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException;}
对应到SpringMVC的Controller方法,read方法即是读取HTTP请求转换为参数对象,write方法即是将返回值对象转换为HTTP响应报文。SpringMVC定义了两个接口来操作这两个过程:参数解析器HandlerMethodArgumentResolver和返回值处理器HandlerMethodReturnValueHandler。
// 参数解析器接口
public interface HandlerMethodArgumentResolver {// 解析器是否支持方法参数boolean supportsParameter(MethodParameter parameter);// 解析HTTP报文中对应的方法参数Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;}// 返回值处理器接口
public interface HandlerMethodReturnValueHandler {// 处理器是否支持返回值类型boolean supportsReturnType(MethodParameter returnType);// 将返回值解析为HTTP响应报文void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;}
参数解析器和返回值处理器在底层处理时,都是通过HttpMessageConverter进行转换。流程如下:
RequestResponseBodyMethodProcessor类实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接口,@RequestBody和@ResponseBody这两个注解的解析均由该类来实现。
2、参数解析
在上文中我们介绍了getMethodArgumentValues方法实现了参数解析但并未深入解析,这里我们进行下源码分析
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];// 遍历所有参数,逐个解析for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = resolveProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;} // 通过supportsParameter方法寻找参数合适的解析器if (this.argumentResolvers.supportsParameter(parameter)) {try {args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}}if (args[i] == null) {throw new IllegalStateException("Could not resolve method parameter at index " +parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));}}return args;}
2.1、查找解析器
HandlerMethodArgumentResolverComposite类实现了supportsParameter方法,后续调用argumentResolverCache即参数解析器
private final List<HandlerMethodArgumentResolver> argumentResolvers =new LinkedList<HandlerMethodArgumentResolver>();@Overridepublic boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) != null;}private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;}
我们直接进入HandlerMethodArgumentResolver接口的实现类,也正是我们上问提到的RequestResponseBodyMethodProcessor来看看
@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(RequestBody.class);}
可以看到这里实际上就是判断方法上是否有@RequestBody,这就印证了我们所说的@RequestBody由RequestResponseBodyMethodProcessor来实现解析。
2.2、解析过程
已经通过supportsParameter确定了由RequestResponseBodyMethodProcessor类来进行解析,下面再看如何解析,于是我们跟着源码进入resolveArgument方法。
@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {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);}
其核心内容为通过readWithMessageConverters读取HTTP的请求体内容,而该方法由AbstractMessageConverterMethodArgumentResolver负责实现。
// RequestResponseBodyMethodProcessor.java@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();// 读取HTTP报文Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);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());}}mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());return adaptArgumentIfNecessary(arg, parameter);}@Overrideprotected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);//调用AbstractMessageConverterMethodArgumentResolver中的实现Object arg = readWithMessageConverters(inputMessage, parameter, paramType);if (arg == null) {if (checkRequired(parameter)) {throw new HttpMessageNotReadableException("Required request body is missing: " +parameter.getMethod().toGenericString());}}return arg;}
这里的关键部分即是遍历所有的HttpMessageConverter,通过canRead方法判断转换器是否支持对参数的转换,然后执行read方法完成转换。
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {....try {inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();....// 判断转换器是否支持参数类型if (converter.canRead(targetClass, contentType)) {if (inputMessage.getBody() != null) {inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);// read方法执行HTTP报文到参数的转换body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);}break;}...}}catch (IOException ex) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex);}....return body;
}
3、返回值处理
完成Controller方法的调用后,在ServletInvocableHandlerMethod的invokeAndHandle中,使用返回值处理器对返回值进行转换。
这里的returnValueHandlers也是HandlerMethodReturnValueHandler的集合体HandlerMethodReturnValueHandlerComposite
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 请求处理Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 返回值处理this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}
3.1、选择处理器
handleReturnValue首先会通过selectHandler找出支持处理当前返回值的处理器,然后再调用该处理器的handleReturnValue方法进行真正的处理。
//HandlerMethodReturnValueHandlerComposite.java@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 选择合适的HandlerMethodReturnValueHandler,如果没有用@ResposeBody注解和用了注解其返回值处理器肯定不同HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}// 执行返回值处理handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}
而selectHandler方法遍历所有HandlerMethodReturnValueHandler,调用其supportsReturnType方法选择合适的HandlerMethodReturnValueHandler,然后调用其handleReturnValue方法完成处理。
//HandlerMethodReturnValueHandlerComposite.javaprivate HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}// 判断当前的HandlerMethodReturnValueHandler是否支持处理返回值if (handler.supportsReturnType(returnType)) {return handler;}}return null;}
RequestResponseBodyMethodProcessor要求方法上有@ResponseBody注解或者方法所在的Controller类上有@ResponseBody的注解。这就是常常用@RestController注解代替@Controller注解的原因,因为@RestController注解自带@ResponseBody。
@Overridepublic boolean supportsReturnType(MethodParameter returnType) {return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));}
3.2、处理过程
handleReturnValue方法实际也是调用HttpMessageConverter来完成转换处理
//RequestResponseBodyMethodProcessor.java@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// 调用HttpMessageConverter执行writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}
AbstractMessageConverterMethodProcessor使用canWrite方法选择合适的HttpMessageConverter,然后调用write方法完成转换。
//AbstractMessageConverterMethodProcessor.java
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {....if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> messageConverter : this.messageConverters) {// 判断是否支持返回值类型,返回值类型很有可能不同,如String,Map,List等if (messageConverter.canWrite(valueType, selectedMediaType)) {outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),inputMessage, outputMessage);if (outputValue != null) {addContentDispositionHeader(inputMessage, outputMessage);// 执行返回值转换((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);...}return;}}}....
}
补充
上文中提到的解析器和转换器都是在适配器初始化的时候添加的。
//RequestMappingHandlerAdapter.javapublic RequestMappingHandlerAdapter() {StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(stringHttpMessageConverter);this.messageConverters.add(new SourceHttpMessageConverter<Source>());this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
相关文章:

SpringMVC源码:参数解析、方法调用与返回值处理
参考资料: 《SpringMVC源码解析系列》 《SpringMVC源码分析》 《Spring MVC源码》 写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。 前文: 《SpringMVC源码&a…...

【MySQL】表的数据处理
哈喽,大家好!我是保护小周ღ,本期为大家带来的是 MySQL 数据表中数据的基本处理的操作,数据表的增删改查,更多相关知识敬请期待:保护小周ღ *★,*:.☆( ̄▽ ̄)/$:*.★*一、 添加数据&a…...

反思当下所处的环境,有没有让你停滞不前、随波逐流
环境对人的影响真的很大,小时候的环境、长大后的环境、工作环境、生活环境、好的环境、差的环境......我们都生活在一定的环境中所以既是环境的产物,又是环境的创造者与改造者。我们与环境的关系是相辅相成的我们的生活和工作当中接触到的人或事或物&…...
小程序(十四)后端-签到成功
文章目录一、持久层1、CheckinMapper.xml2、CheckinMapper.java3、TbHolidaysDao.xml4、TbHolidaysDao.java5、TbWorkdayDao.xml6、TbWorkdayDao.java二、业务层1、 CheckinService.java三、conroller层1、编写 TbUserDao.xml 文件,查询员工的入职日期。2、编写 TbU…...

X264简介-Android使用(一)
X264 简介及使用 1、简介 2、环境搭建 3、使用 4、小结 简介 官网连接:https://www.videolan.org/developers/x264.html 官方文档:https://wiki.videolan.org/Category:X264/ x264是用于编码H.264/MPEG-4 AVC视频流的免费软件库。它世界上最流行的…...

DetectGPT:使用概率曲率的零样本机器生成文本检测
DetectGPT的目的是确定一段文本是否由特定的llm生成,例如GPT-3。为了对段落 x 进行分类,DetectGPT 首先使用通用的预训练模型(例如 T5)对段落 ~xi 生成较小的扰动。然后DetectGPT将原始样本x的对数概率与每个扰动样本~xi进行比较。…...

【深度学习】BERT变体—BERT-wwm
1.BERT-wwm 1-1 Whole Word Masking Whole Word Masking (wwm)是谷歌在2019年5月31日发布的一项BERT的升级版本,主要更改了原预训练阶段的训练样本生成策略。 原有基于WordPiece的分词方式会把一个完整的词切分成若干个子词,在生成训练样本时ÿ…...
【华为OD机试真题 java、python、c++】优秀学员统计【2022 Q4 100分】(100%通过)
代码请进行一定修改后使用,本代码保证100%通过率。本文章提供java、python、c++三种代码 题目描述 公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。 每个员工会对应一个id,每天的打卡记录记录当…...
JavaScript里的循环方法:forEach,for-in,for-of
文章目录forEach循环for–in循环for-of循环for-of循环使用例子:循环一个数组(Array):循环一个字符串:循环一个类型化的数组(TypedArray):循环一个Map:循环一个 Set:循环一个 DOM collection循环一个拥有enumerable属性的对象循环一个生成器(g…...

汽车标定知识整理(二):CCP报文基本命令介绍
目录 一、基本命令 CRO命令报文的基本命令表: 二、基本命令与可选命令帧格式介绍 1、CONNECT——建立连接(0x01) 2、GET_CPP_VERSION——获取CCP版本(0x1B) 3、SET_MTA——设置内存传输地址(0x02&#…...

windows系统安装Linux虚拟机教程
虚拟机的安装首先要下载虚拟机的安装包,当前最新版本是VMware 16.2.1。软件我都已经给大家准备好了(含序列号),大家在这里下载就好。虚拟机安装包下载完毕之后,将它安装到电脑里。这个安装过程很简单,一路下…...
“基于Spring Cloud Alibaba的微服务架构实战:Nacos配置管理“
引言 Spring Cloud Alibaba 是 Spring Cloud 和 Alibaba 集团联合推出的开源微服务框架,旨在为 Java 开发者提供一种简单、易用、高效的微服务解决方案。Nacos 是一个面向云原生应用的动态服务发现、配置管理和服务管理平台,提供了服务注册与发现、配置管…...
【Linux】常见面试题
1. 查看文件内容 cat tail head less more 2. 几个查看文件内容的命令有什么区别 cat 文件名 # 将文件内的内容全部打印出来,cat 文件1 文件2 先将文件1全部法印,然后在打印文件2 more 文件名 # 分屏查看 less 文件名 # 上下分页查看 head 文件…...

【数据结构】顺序表:尾部操作我很行,随机访问我超快!!!
顺序表的模拟实现 文章目录顺序表的模拟实现1.线性表2.顺序表2.1概念结构2.2顺序表的模拟实现2.2.1顺序表的初始化2.2.2顺序表的销毁2.2.3尾插数据2.2.4尾删数据2.2.5头插数据2.2.6头删数据2.2.7中间插入数据2.2.8中间删除数据2.2.9打印顺序表2.2.10查找数据2.2.11复用Insert和…...
SQL优化
SQL优化 SQL优化的方法: sql查询语句尽不使用select * ,而是具体的字段。 节约资源,减少网络开销。减少回表,提高查询效率。 避免在where子句中使用or来连接条件。 or可能会使索引失效,从而进行全表查询。 尽量使用…...
Java ArrayList 和 LinkList 原理对比
Java 中的 ArrayList 和 LinkedList 都是实现了 List 接口的集合类它们都允许添加、删除和修改元素。但是它们的底层实现原理不同导致它们在不同的场景下拥有不同的优劣势。 ArrayListArrayList 的底层是通过数组实现的因此它具有数组的特性。它允许快速随机访问元素但是在插入…...

【Spring】入门概述(一)
🚗Spring学习第一站~ 🚩本文已收录至专栏:Spring家族学习之旅 👍希望您能有所收获 一.初识 Spring并不是单一的一个技术,而是一个大家族,发展到今天已经形成了一种开发的生态圈,Spring提供了若…...

十二、面向切面编程AOP
IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能,把它转化成组件。 AOP(Aspect Oriented Programming):面向切面编程,面向方面编程。(AOP是一种编程技术) AOP是对OOP的补充延伸。 …...
Mybatis 处理 CLOB/BLOB 类型数据
Mybatis 处理 CLOB/BLOB 类型数据 BLOB 和 CLOB 都是大型字段类型。 BLOB通过二进制存储,而CLOB可以直接存储文本。 通常,图片、文件、音乐等信息存储在 BLOB 字段中。首先,文件是转换为二进制,然后存储在。文章或较长的文本存…...

【NLP经典论文阅读】Efficient Estimation of Word Representations in Vector Space(附代码)
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...