作者笔记仓库:https://github.com/seazean/javanotes
欢迎各位关注我的笔记仓库,clone 仓库到本地后使用 Typora 阅读效果更好,因为有目录侧边栏能更好的看出层级。
请求进入原生的 HttpServlet 的 doGet() 方法处理,调用子类 FrameworkServlet 的 doGet() 方法,最终调用 DispatcherServlet 的 doService() 方法,为请求设置相关属性后调用 doDispatch(),请求和响应的以参数的形式传入
//request 和 response 为 Java 原生的类 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 { processedRequest = checkMultipart(request); //文件上传请求 multipartRequestParsed = (processedRequest != request); // 找到当前请求使用哪个 HandlerMapping(Controller的方法)处理,返回执行链 mappedHandler = getHandler(processedRequest); // 没有合适的处理请求的方式 HandlerMapping 直接返回 if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } //根据映射器获取当前 handler 处理器适配器,用来处理当前的请求 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 获取发出此次请求的方法 String method = request.getMethod(); // 判断请求是不是 GET 方法 boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 执行处理方法,返回的是 ModelAndView 对象,封装了所有的返回值数据 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 设置视图名字 applyDefaultViewName(processedRequest, mv); // 执行拦截器链中的方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } // 处理 程序调用的结果,进行结果派发 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } //.... }
笔记参考视频:https://www.bilibili.com/video/BV19K4y1L7MT
doDispatch() 中调用 getHandler 方法获取所有的映射器
总体流程:
所有的请求映射都在 HandlerMapping 中,RequestMappingHandlerMapping 处理 @RequestMapping 注解的所有映射规则
遍历所有的 HandlerMapping 看是否可以匹配当前请求,匹配成功后返回,匹配失败设置 HTTP 404 响应码
用户可以自定义的映射处理,也可以给容器中放入自定义 HandlerMapping
访问 URL:http://localhost:8080/user
@GetMapping("/user") public String getUser(){ return "GET"; }
HandlerMapping 处理器映射器,保存了所有 @RequestMapping
和 handler
的映射规则
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { //遍历所有的 HandlerMapping for (HandlerMapping mapping : this.handlerMappings) { //尝试去每个 HandlerMapping 中匹配当前请求的处理 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
mapping.getHandler(request)
:调用 AbstractHandlerMapping#getHandler
Object handler = getHandlerInternal(request)
:获取映射器,底层调用 RequestMappingInfoHandlerMapping 类的方法,又调用 AbstractHandlerMethodMapping#getHandlerInternal
String lookupPath = initLookupPath(request)
:地址栏的 uri,这里的 lookupPath 为 /user
this.mappingRegistry.acquireReadLock()
:防止并发
handlerMethod = lookupHandlerMethod(lookupPath, request)
:获取当前 HandlerMapping 中的映射规则
AbstractHandlerMethodMapping.lookupHandlerMethod():
directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath)
:获取当前的映射器与当前请求的 URI 有关的所有映射规则
addMatchingMappings(directPathMatches, matches, request)
:匹配某个映射规则
for (T mapping : mappings)
:遍历所有的映射规则match = getMatchingMapping(mapping, request)
:去匹配每一个映射规则,匹配失败返回 nullmatches.add(new Match())
:匹配成功后封装成匹配器添加到匹配集合中Match bestMatch = matches.get(0)
:匹配完成只剩一个,直接获取返回对应的处理方法
if (matches.size() > 1)
:当有多个映射规则符合请求时,报错
return bestMatch.getHandlerMethod()
:返回匹配器中的处理方法
executionChain = getHandlerExecutionChain(handler, request)
:为当前请求和映射器的构建一个拦截器链
return executionChain
:返回拦截器链,包含 HandlerMapping 和拦截方法
doDispatch() 中 调用 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { //遍历所有的 HandlerAdapter for (HandlerAdapter adapter : this.handlerAdapters) { //判断当前适配器是否支持当前 handle //return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)) //这里返回的是True, if (adapter.supports(handler)) { //返回的是 RequestMappingHandlerAdapter return adapter; } } } throw new ServletException(); }
实例代码:
@GetMapping("/params") public String param(Map<String, Object> map, Model model, HttpServletRequest request) { map.put("k1", "v1"); //都可以向请求域中添加数据 model.addAttribute("k2", "v2"); //它们两个都在数据封装在 BindingAwareModelMap request.setAttribute("m", "HelloWorld"); return "forward:/success"; }
doDispatch() 中调用 mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
执行目标方法
AbstractHandlerMethodAdapter#handle
→ RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 是否是 Session 加锁 if (this.synchronizeOnSession) { } else { //使用适配器执行方法 mav = invokeHandlerMethod(request, response, handlerMethod); } //是否在响应头中设置了缓存的属性 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { //是否通过 SessionAttributes 设置了会话属性 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { //使用给定的缓存秒数并生成相应的 HTTP 标头 applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { //根据此生成器的设置准备接受的响应,设置缓存的时间 prepareResponse(response); } } return mav; }
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //封装成 SpringMVC 的接口,用于通用 Web 请求拦截器,使能够访问通用请求元数据,而不是用于实际处理请求 ServletWebRequest webRequest = new ServletWebRequest(request, response); try { //WebDataBinder 用于从 Web 请求参数到 JavaBean 对象的数据绑定,获取创建该实例的工厂 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); //创建 Model 实例,用于向模型添加属性 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); //方法执行器 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); //参数解析器,有很多 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } //返回值处理器,也有很多 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } //设置数据绑定器 invocableMethod.setDataBinderFactory(binderFactory); //设置参数检查器 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); //新建一个 ModelAndViewContainer 并进行初始化和一些属性的填充 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //设置一些属性 //执行目标方法 invocableMethod.invokeAndHandle(webRequest, mavContainer); //异步请求 if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 获取 ModelAndView 对象,封装了 ModelAndViewContainer return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
ServletInvocableHandlerMethod#invokeAndHandle:执行目标方法
returnValue = invokeForRequest(webRequest, mavContainer, providedArgs)
:执行自己写的 controller 方法,返回的就是自定义方法中 return 的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs)
:参数处理的逻辑,遍历所有的参数解析器解析参数或者将 URI 中的参数进行绑定,绑定完成后开始执行目标方法
parameters = getMethodParameters()
:获取此处理程序方法的方法参数的详细信息
Object[] args = new Object[parameters.length]
:存放所有的参数
for (int i = 0; i < parameters.length; i++)
:遍历所有的参数
args[i] = findProvidedArgument(parameter, providedArgs)
:获取调用方法时提供的参数,一般是空
if (!this.resolvers.supportsParameter(parameter))
:寻找可以解析当前参数的参数解析器
return getArgumentResolver(parameter) != null
:获取参数的解析是否为空
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers)
:遍历容器内所有的解析器
if (resolver.supportsParameter(parameter))
:是否支持当前参数
PathVariableMethodArgumentResolver#supportsParameter
:解析标注 @PathVariable 注解的参数
ModelMethodProcessor#supportsParameter
:解析 Map 类型的参数
ModelMethodProcessor#supportsParameter
:解析 Model 类型的参数,Model 和 Map 的作用一样
ExpressionValueMethodArgumentResolver#supportsParameter
:解析标注 @Value 注解的参数
RequestParamMapMethodArgumentResolver#supportsParameter
:解析标注 @RequestParam 注解
ModelAttributeMethodProcessor#supportsParameter
:解析标注 @ModelAttribute 注解或者不是简单类型
args[i] = this.resolvers.resolveArgument()
:开始解析参数,每个参数使用的解析器不同
resolver = getArgumentResolver(parameter)
:获取参数解析器
return resolver.resolveArgument()
:开始解析
PathVariableMapMethodArgumentResolver#resolveArgument
:@PathVariable,包装 URI 中的参数为 MapMapMethodProcessor#resolveArgument
:调用 mavContainer.getModel()
返回默认的 BindingAwareModelMap 对象ModelAttributeMethodProcessor#resolveArgument
:自定义的 JavaBean 的绑定封装,下一小节详解return doInvoke(args)
:真正的执行方法
Method method = getBridgedMethod()
:从 HandlerMethod 获取要反射执行的方法ReflectionUtils.makeAccessible(method)
:破解权限method.invoke(getBean(), args)
:执行方法,getBean 获取的是标记 @Controller 的 Bean 类,其中包含执行方法进行返回值的处理,响应部分详解,处理完成进入下面的逻辑
RequestMappingHandlerAdapter#getModelAndView:获取 ModelAndView 对象
modelFactory.updateModel(webRequest, mavContainer)
:Model 数据升级到会话域(请求域中的数据在重定向时丢失)
updateBindingResult(request, defaultModel)
:把绑定的数据添加到 Model 中
if (mavContainer.isRequestHandled())
:判断请求是否已经处理完成了
ModelMap model = mavContainer.getModel()
:获取包含 Controller 方法参数的 BindingAwareModelMap 对象(本节开头)
mav = new ModelAndView()
:把 ModelAndViewContainer 和 ModelMap 中的数据封装到 ModelAndView
if (!mavContainer.isViewReference())
:视图是否是通过名称指定视图引用
if (model instanceof RedirectAttributes)
:判断 model 是否是重定向数据,如果是进行重定向逻辑
return mav
:任何方法执行都会返回 ModelAndView 对象
解析自定义的 JavaBean 为例
Person.java:
@Data @Component //加入到容器中 public class Person { private String userName; private Integer age; private Date birth; }
Controller:
@RestController //返回的数据不是页面 public class ParameterController { // 数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定 @GetMapping("/saveuser") public Person saveuser(Person person){ return person; } }
访问 URL:http://localhost:8080/saveuser?userName=zhangsan&age=20
进入源码:ModelAttributeMethodProcessor#resolveArgument
name = ModelFactory.getNameForParameter(parameter)
:获取名字,此例就是 person
ann = parameter.getParameterAnnotation(ModelAttribute.class)
:是否有 ModelAttribute 注解
if (mavContainer.containsAttribute(name))
:ModelAndViewContainer 中是否包含 person 对象
attribute = createAttribute()
:创建一个实例,空的 Person 对象
binder = binderFactory.createBinder(webRequest, attribute, name)
:Web 数据绑定器,可以利用 Converters 将请求数据转成指定的数据类型,绑定到 JavaBean 中
bindRequestParameters(binder, webRequest)
:利用反射向目标对象填充数据
servletBinder = (ServletRequestDataBinder) binder
:类型强转
servletBinder.bind(servletRequest)
:绑定数据
mpvs = new MutablePropertyValues(request.getParameterMap())
:获取请求 URI 参数中的 KV 键值对
addBindValues(mpvs, request)
:子类可以用来为请求添加额外绑定值
doBind(mpvs)
:真正的绑定的方法,调用 applyPropertyValues
应用参数值,然后调用 setPropertyValues
方法
AbstractPropertyAccessor#setPropertyValues()
:
List<PropertyValue> propertyValues
:获取到所有的参数的值,就是 URI 上的所有的参数值
for (PropertyValue pv : propertyValues)
:遍历所有的参数值
setPropertyValue(pv)
:填充到空的 Person 实例中
nestedPa = getPropertyAccessorForPropertyPath(propertyName)
:获取属性访问器
tokens = getPropertyNameTokens()
:获取元数据的信息
nestedPa.setPropertyValue(tokens, pv)
:填充数据
processLocalProperty(tokens, pv)
:处理属性
if (!Boolean.FALSE.equals(pv.conversionNecessary))
:数据是否需要转换了
if (pv.isConverted())
:数据已经转换过了,转换了直接赋值,没转换进行转换
oldValue = ph.getValue()
:获取未转换的数据
valueToApply = convertForProperty()
:进行数据转换
TypeConverterDelegate#convertIfNecessary
:进入该方法的逻辑
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor))
:判断能不能转换
GenericConverter converter = getConverter(sourceType, targetType)
:获取类型转换器
converter = this.converters.find(sourceType, targetType)
:寻找合适的转换器
sourceCandidates = getClassHierarchy(sourceType.getType())
:原数据类型
targetCandidates = getClassHierarchy(targetType.getType())
:目标数据类型
for (Class<?> sourceCandidate : sourceCandidates) { //双重循环遍历,寻找合适的转换器 for (Class<?> targetCandidate : targetCandidates) {
GenericConverter converter = getRegisteredConverter(..)
:匹配类型转换器
return converter
:返回转换器
conversionService.convert(newValue, sourceTypeDesc, typeDescriptor)
:开始转换
converter = getConverter(sourceType, targetType)
:获取可用的转换器result = ConversionUtils.invokeConverter()
:执行转换方法
converter.convert()
:调用转换器的转换方法(GenericConverter#convert)return handleResult(sourceType, targetType, result)
:返回结果ph.setValue(valueToApply)
:设置 JavaBean 属性(BeanWrapperImpl.BeanPropertyHandler)
Method writeMethod
:获取 set 方法
Class<?> cls = getClass0()
:获取 Class 对象writeMethodName = Introspector.SET_PREFIX + getBaseName()
:set 前缀 + 属性名writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args)
:获取只包含一个参数的 set 方法setWriteMethod(writeMethod)
:加入缓存ReflectionUtils.makeAccessible(writeMethod)
:设置访问权限writeMethod.invoke(getWrappedInstance(), value)
:执行方法bindingResult = binder.getBindingResult()
:获取绑定的结果
mavContainer.addAllAttributes(bindingResultModel)
:把所有填充的参数放入 ModelAndViewContainer
return attribute
:返回填充后的 Person 对象
以 Person 为例:
@ResponseBody //利用返回值处理器里面的消息转换器进行处理 @GetMapping(value = "/person") public Person getPerson(){ Person person = new Person(); person.setAge(28); person.setBirth(new Date()); person.setUserName("zhangsan"); return person; }
直接进入方法执行完后的逻辑 ServletInvocableHandlerMethod#invokeAndHandle:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 执行目标方法,return person 对象 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 设置状态码 setResponseStatus(webRequest); // 判断方法是否有返回值 if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } //返回值是字符串 else if (StringUtils.hasText(getResponseStatusReason())) { //设置请求处理完成 mavContainer.setRequestHandled(true); return; // 设置请求没有处理完成,还需要进行返回值的逻辑 mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 返回值的处理 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) {} }
没有加 @ResponseBody 注解的返回数据按照视图(页面)处理的逻辑,ViewNameMethodReturnValueHandler(视图详解)
此例是加了注解的,返回的数据不是视图,HandlerMethodReturnValueHandlerComposite#handleReturnValue:
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) { //获取合适的返回值处理器 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException(); } //使用处理器处理返回值(详解源码中的这两个函数) handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
HandlerMethodReturnValueHandlerComposite#selectHandler:
boolean isAsyncValue = isAsyncReturnValue(value, returnType)
:是否是异步请求
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers)
:遍历所有的返回值处理器
RequestResponseBodyMethodProcessor#supportsReturnType
:处理标注 @ResponseBody 注解的返回值ModelAndViewMethodReturnValueHandler#supportsReturnType
:处理返回值类型是 ModelAndView 的处理器ModelAndViewResolverMethodReturnValueHandler#supportsReturnType
:直接返回 true,处理所有数据RequestResponseBodyMethodProcessor#handleReturnValue:处理返回值
mavContainer.setRequestHandled(true)
:设置请求处理完成
inputMessage = createInputMessage(webRequest)
:获取输入的数据
outputMessage = createOutputMessage(webRequest)
:获取输出的数据
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage)
:使用消息转换器进行写出
if (value instanceof CharSequence)
:判断返回的数据是不是字符类型
body = value
:把 value 赋值给 body,此时 body 中就是填充后的 Person 对象
if (isResourceType(value, returnType))
:当前数据是不是流数据
MediaType selectedMediaType
:内容协商后选择使用的类型,浏览器和服务器都支持的媒体(数据)类型
MediaType contentType = outputMessage.getHeaders().getContentType()
:获取响应头的数据
if (contentType != null && contentType.isConcrete())
:判断当前响应头中是否已经有确定的媒体类型
selectedMediaType = contentType
:说明前置处理已经使用了媒体类型,直接继续使用该类型
acceptableTypes = getAcceptableMediaTypes(request)
:获取浏览器支持的媒体类型,请求头字段
this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request))
:调用该方法
for(ContentNegotiationStrategy strategy:this.strategies)
:默认策略是提取请求头的字段的内容,策略类为HeaderContentNegotiationStrategy,可以配置添加其他类型的策略List<MediaType> mediaTypes = strategy.resolveMediaTypes(request)
:解析 Accept 字段存储为 List
headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT)
:获取请求头中 Accept 字段List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues)
:解析成 List 集合MediaType.sortBySpecificityAndQuality(mediaTypes)
:按照相对品质因数 q 降序排序producibleTypes = getProducibleMediaTypes(request, valueType, targetType)
:服务器能生成的媒体类型
request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE)
:从请求域获取默认的媒体类型 for (HttpMessageConverter<?> converter : this.messageConverters)
:遍历所有的消息转换器converter.canWrite(valueClass, null)
:是否支持当前的类型 result.addAll(converter.getSupportedMediaTypes())
:把当前 MessageConverter 支持的所有类型放入 resultList<MediaType> mediaTypesToUse = new ArrayList<>()
:存储最佳匹配
内容协商:
for (MediaType requestedType : acceptableTypes) { //遍历所有的浏览器能接受的媒体类型 for (MediaType producibleType : producibleTypes) { //遍历所有服务器能产出的 if (requestedType.isCompatibleWith(producibleType)) { //判断类型是否匹配,最佳匹配 //数据协商匹配成功 mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } }
MediaType.sortBySpecificityAndQuality(mediaTypesToUse)
:按照相对品质因数 q 排序,降序排序,越大的越好
for (MediaType mediaType : mediaTypesToUse)
:遍历所有的最佳匹配
selectedMediaType = mediaType
:赋值给选择的类型
selectedMediaType = selectedMediaType.removeQualityValue()
:媒体类型去除相对品质因数
for (HttpMessageConverter<?> converter : this.messageConverters)
:遍历所有的 HTTP 数据转换器
GenericHttpMessageConverter genericConverter
:MappingJackson2HttpMessageConverter 可以将对象写为 JSON
((GenericHttpMessageConverter) converter).canWrite()
:转换器是否可以写出给定的类型
AbstractJackson2HttpMessageConverter#canWrit
if (!canWrite(mediaType))
:是否可以写出指定类型
MediaType.ALL.equalsTypeAndSubtype(mediaType)
:是不是 */*
类型getSupportedMediaTypes()
:支持 application/json
和 application/*+json
两种类型return true
:返回 trueobjectMapper = selectObjectMapper(clazz, mediaType)
:选择可以使用的 objectMappercauseRef = new AtomicReference<>()
:获取并发安全的引用if (objectMapper.canSerialize(clazz, causeRef))
:objectMapper 可以序列化当前类return true
:返回 true body = getAdvice().beforeBodyWrite()
:要响应的所有数据,Person 对象
addContentDispositionHeader(inputMessage, outputMessage)
:检查路径
genericConverter.write(body, targetType, selectedMediaType, outputMessage)
:调用消息转换器的 write 方法
AbstractGenericHttpMessageConverter#write
:该类的方法
addDefaultHeaders(headers, t, contentType)
:设置响应头中的数据类型
writeInternal(t, type, outputMessage)
:真正的写出数据的函数
Object value = object
:value 引用 Person 对象ObjectWriter objectWriter = objectMapper.writer()
:获取用来输出 JSON 对象的 ObjectWriterobjectWriter.writeValue(generator, value)
:写出数据为 JSON开启基于请求参数的内容协商模式:(SpringBoot 方式)
spring.mvc.contentnegotiation:favor-parameter: true #开启请求参数内容协商模式
发请求: http://localhost:8080/person?format=json,解析 format
策略类为 ParameterContentNegotiationStrategy,运行流程如下:
acceptableTypes = getAcceptableMediaTypes(request)
:获取浏览器支持的媒体类型
mediaTypes = strategy.resolveMediaTypes(request)
:解析请求 URL 参数中的数据
return resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest))
:
getMediaTypeKey(webRequest)
:
request.getParameter(getParameterName())
:获取 URL 中指定的需求的数据类型
getParameterName()
:获取参数的属性名 formatgetParameter()
:获取 URL 中 format 对应的数据resolveMediaTypeKey()
:解析媒体类型,封装成集合
自定义内容协商策略:
public class WebConfig implements WebMvcConfigurer { @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override //自定义内容协商策略 public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { Map<String, MediaType> mediaTypes = new HashMap<>(); mediaTypes.put("json", MediaType.APPLICATION_JSON); mediaTypes.put("xml",MediaType.APPLICATION_XML); mediaTypes.put("person",MediaType.parseMediaType("application/x-person")); //指定支持解析哪些参数对应的哪些媒体类型 ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes); //请求头解析 HeaderContentNegotiationStrategy headStrategy = new HeaderContentNegotiationStrategy(); //添加到容器中,即可以解析请求头 又可以解析请求参数 configurer.strategies(Arrays.asList(parameterStrategy,headStrategy)); } @Override //自定义消息转换器 public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new GuiguMessageConverter()); } } } }
也可以自定义 HttpMessageConverter,实现 HttpMessageConverter
请求处理:
@GetMapping("/params") public String param(){ return "forward:/success"; //return "redirect:/success"; }
进入执行方法逻辑 ServletInvocableHandlerMethod#invokeAndHandle,进入 this.returnValueHandlers.handleReturnValue
:
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) { //获取合适的返回值处理器:调用 if (handler.supportsReturnType(returnType))判断是否支持 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException(); } //使用处理器处理返回值 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
ViewNameMethodReturnValueHandler#supportsReturnType
public boolean supportsReturnType(MethodParameter returnType) { Class<?> paramType = returnType.getParameterType(); //返回值是否是void 或者 是 CharSequence 字符序列 return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType)); }
ViewNameMethodReturnValueHandler#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 返回值是字符串,是 return "forward:/success" if (returnValue instanceof CharSequence) { String viewName = returnValue.toString(); //把视图名称设置进入 ModelAndViewContainer 中 mavContainer.setViewName(viewName); //判断是否是重定向数据 `viewName.startsWith("redirect:")` if (isRedirectViewName(viewName)) { //如果是重定向,设置是重定向指令 mavContainer.setRedirectModelScenario(true); } } else if (returnValue != null) { // should not happen throw new UnsupportedOperationException(); } }
doDispatch()中的 processDispatchResult:处理派发结果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; if (exception != null) { } // mv 是 ModelAndValue if (mv != null && !mv.wasCleared()) { // 渲染视图 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else {} }
DispatcherServlet#render:
Locale locale = this.localeResolver.resolveLocale(request)
:国际化相关
String viewName = mv.getViewName()
:视图名字,是请求转发 forward:/success(响应数据部分解析了该名字存入 ModelAndView 是通过 ViewNameMethodReturnValueHandler)
view = resolveViewName(viewName, mv.getModelInternal(), locale, request)
:解析视图
for (ViewResolver viewResolver : this.viewResolvers)
:遍历所有的视图解析器
view = viewResolver.resolveViewName(viewName, locale)
:根据视图名字解析视图,调用内容协商视图处理器 ContentNegotiatingViewResolver 的方法
attrs = RequestContextHolder.getRequestAttributes()
:获取请求的相关属性信息
requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest())
:获取最佳匹配的媒体类型,函数内进行了匹配的逻辑
candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes)
:获取候选的视图对象
for (ViewResolver viewResolver : this.viewResolvers)
:遍历所有的视图解析器
View view = viewResolver.resolveViewName(viewName, locale)
:解析视图
AbstractCachingViewResolver#resolveViewName
:调用此方法
请求转发:实例为 InternalResourceView
returnview = createView(viewName, locale)
:UrlBasedViewResolver#createView
if (viewName.startsWith(FORWARD_URL_PREFIX))
:视图名字是否是 forward:
的前缀
forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length())
:名字截取前缀
view = new InternalResourceView(forwardUrl)
:新建 InternalResourceView 对象并返回
return applyLifecycleMethods(FORWARD_URL_PREFIX, view)
:Spring 中的初始化操作
重定向:实例为 RedirectView
returnview = createView(viewName, locale)
:UrlBasedViewResolver#createView
if (viewName.startsWith(REDIRECT_URL_PREFIX))
:视图名字是否是 redirect:
的前缀redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length())
:名字截取前缀RedirectView view = new RedirectView()
:新建 RedirectView 对象并返回bestView = getBestView(candidateViews, requestedMediaTypes, attrs)
:选出最佳匹配的视图对象
view.render(mv.getModelInternal(), request, response)
:页面渲染
mergedModel = createMergedOutputModel(model, request, response)
:把请求域中的数据封装到 Map
prepareResponse(request, response)
:响应前的准备工作,设置一些响应头
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response)
:渲染输出的数据
请求转发 InternalResourceView 的逻辑:
getRequestToExpose(request)
:获取 Servlet 原生的方式exposeModelAsRequestAttributes(model, request)
:暴露 model 作为请求域的属性
model.forEach()
:遍历 Model 中的数据request.setAttribute(name, value)
:设置到请求域中exposeHelpers(request)
:自定义接口dispatcherPath = prepareForRendering(request, response)
:确定调度分派的路径,此例是 /successrd = getRequestDispatcher(request, dispatcherPath)
:获取 Servlet 原生的 RequestDispatcher 实现转发rd.forward(request, response)
:实现请求转发重定向 RedirectView 的逻辑:
targetUrl = createTargetUrl(model, request)
:获取目标 URL
enc = request.getCharacterEncoding()
:设置编码 UTF-8
appendQueryProperties(targetUrl, model, enc)
:添加一些属性,比如 url + ?name=123&&age=324
sendRedirect(request, response, targetUrl, this.http10Compatible)
:重定向
response.sendRedirect(encodedURL)
:使用 Servlet 原生方法实现重定向