上海大型网站制作/微信公众号怎么推广
参考资料:
《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学习相关,读研读博…...

Spring bean生命周期分为几个阶段?
bean 的生命周期从调用 beanFactory 的 getBean 开始,到这个 bean 被销毁,可以总结为以下七个阶段:处理名称,检查缓存→处理父子容器→处理 dependsOn→选择 scope 策略→创建 bean→类型转换处理→销毁 bean划分的阶段和名称并不…...

【基础算法】单链表的OJ练习(4) # 分割链表 # 回文链表 #
文章目录前言分割链表回文链表写在最后前言 本章的OJ练习相对前面的难度加大了,但是换汤不换药,还是围绕单链表的性质来出题的。我相信,能够过了前面的OJ练习,本章的OJ也是轻轻松松。 对于OJ练习(3):-> 传送门 <…...

SpringBoot整合定时任务和邮件发送(邮箱 信息轰炸 整蛊)
SpringBoot整合定时任务和邮件发送(邮箱 信息轰炸 整蛊) 目录SpringBoot整合定时任务和邮件发送(邮箱 信息轰炸 整蛊)1.概述2.最佳实践2.1创建项目引入依赖(mail)2.2 修改yml配置文件2.3 启动类添加EnableScheduling注解2.4 执行的…...

Arduino添加ESP32开发板
【2023年3月4日】 最近要在新电脑上安装Arduino,需要进行一些配置,正好记录一下! Arduino2.0.1 下的开发板添加操作。 ESP32开发板GitHub链接: GitHub - espressif/arduino-esp32: Arduino core for the ESP32Arduino core for…...

Mysql通配符的使用
LIKE操作符 通配符:用来匹配值的一部分的特殊字符。 搜索模式:由字面值,通配符或两者组合构成的搜索条件。 百分号(%)通配符 搜索模式使用例如下 SELECT prod_id, prod_name FROM products WHERE prod_name Like jet%; 这条子句表示&…...

RocketMQ-02
1. 案例介绍 1.1 业务分析 模拟电商网站购物场景中的【下单】和【支付】业务 ###1)下单 用户请求订单系统下单订单系统通过RPC调用订单服务下单订单服务调用优惠券服务,扣减优惠券订单服务调用调用库存服务,校验并扣减库存订单服务调用用户…...

深度学习卷积神经网络CNN之 VGGNet模型主vgg16和vgg19网络模型详解说明(理论篇)
1.VGG背景 2. VGGNet模型结构 3. 特点(创新、优缺点及新知识点) 一、VGG背景 VGGNet是2014年ILSVRC(ImageNet Large Scale Visual Recognition Challenge大规模视觉识别挑战赛)竞赛的第二名,解决ImageNet中的1000类图…...

三:BLE协议架构简介
低功耗蓝牙体系整体架构说明1. PHY(物理层)2. LL(链路层)3. HCI(主机与控制器通信接口)4. L2CAP(逻辑链路控制及适配协议)5. ATT(属性协议)6. GATT(通用属性规范)7. GAP(通用访问规范)8. SM(安全管理)整体架构说明 架构层说明PHY1. 物理层2. 控制射频的发送和接收LL1. 链路层2.…...

小型双轮差速底盘双灰度循迹功能的实现
1. 功能说明 在机器人车体上安装2个 灰度传感器 ,实现机器人按照下图所指定的路线进行导航运动,来模拟仓库物流机器人按指定路线行进的工作过程。 2. 使用样机 本实验使用的样机为R023e样机。 3. 功能实现 3.1 电子硬件 在这个示例中,我们采…...

电子签名?玩具罢了!
需要的前置知识:简单的canvas绘制线路过程 let canvas document.getElementById(id); //id为canvas标签元素的id,或通过其它方法获取标签 let ctx canvas.getContext(2d); //规定为2d绘制图片,即确定为2d画笔 ctx.strokeStyle "whit…...