springboot内嵌容器mvc执行过程
2022-01-12 17:40:35 5 举报
AI智能生成
springboot内嵌容器mvc执行过程
作者其他创作
大纲/内容
1. 在容器启动后第一次发起请求时触发Serlvet#init方法初始化<br>
1. 在 "springboot内嵌容器初始化#2.7.9.2.2.5" 时会将DispatcherServlet注册到servlet上下文中
2. 最终会执行的DispatcherServlet#initStrategies方法,原因见链接中的uml图<br>
1. 初始化文件上传相关<br> <font color="#0076b3"> initMultipartResolver(context);</font><br>
1. 从bean容器中取名称为multipartResolver的bean赋值给this#multipartResolver <br> MultipartAutoConfiguration配置中会注册该名称的bean,类型为StandardServletMultipartResolver<br> <font color="#0076b3">this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);</font><br>
2. 初始化国际化相关<br> <font color="#0076b3"> initLocaleResolver(context);</font><br>
1. 从bean容器中获取名称为localeResolver的bean赋值给this#localeResolver <br> 在配置了spring.mvc.locale的时候WebMvcAutoConfiguration中会注册该bean<br> 如果没有渠道localeResolver的bean则使用默认策略给this#localeResolver 赋值为AcceptHeaderLocaleResolver对象<br> <font color="#0076b3">this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);</font><br>
3. 初始化主题相关,用得较少<br> <font color="#0076b3"> initThemeResolver(context);</font><br>
4. 初始化请求处理映射<br> <font color="#0076b3"> initHandlerMappings(context);</font><br>
1. 如果this.detectAllHandlerMappings为true(默认为true)<br> 则从bean容器中取出HandlerMapping类型的bean<br> 放在this.handlerMappings中,会取出右侧5个HandlerMapping bean<br>
1. faviconHandlerMapping(SimpleUrlHandlerMapping)<br>
2. requestMappingHandlerMapping(RequestMappingHandlerMapping)<br> 这个Bean非常的重要,它的继承关系查看链接<br>
1. SpringBoot中引入mvc模块且没有手动开启@EnableWebMvc注解(如果手动开启会Import DelegatingWebMvcConfiguration,就不满足@ConditionalOnMissin gBean(WebMvcConfigurationSupport.class)条件了,建议不要手动开启,除非在非SpringBoot环境中)<br> 则会加载WebMvcAutoConfiguration,WebMvcAutoConfiguration中的静态内部类EnableWebMvcConfiguration(继承自DelegatingWebMvcConfiguration)<br> 里面有定义一个requestMappingHandlerMapping bean<br>
2. 在实例化requestMappingHandlerMapping bean的时候会执行<br> EnableWebMvcConfiguration#requestMappingHandlerMapping方法<br>
1. 执行超类WebMvcConfigurationSupport#requestMappingHandlerMapping方法<br> <font color="#0076b3">return super.requestMappingHandlerMapping();</font>
1. 执行子类EnableWebMvcConfiguration#createRequestMappingHandlerMapping方法<br> 子类的方法中主要是判断this.mvcRegistrations是不是有一个requestMappingHandlerMapping<br> 的对象了,有的话就就不去实例化一个 RequestMappingHandlerMapping 对象了<br><font color="#0076b3"> RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();</font>
2. 为./1中创建的mapping设置拦截器<br> <font color="#0076b3">mapping.setInterceptors(getInterceptors());</font>
1. 实例化一个InterceptorRegistry对象<br> <font color="#0076b3">InterceptorRegistry registry = new InterceptorRegistry();</font>
2. 调用DelegatingWebMvcConfiguration#addInterceptors方法<br> (在DelegatingWebMvcConfiguration配置中通过@Autowired将容器中所有的WebMvcConfigurer配置<br> 添加到了WebMvcConfigurerComposite类型的configurers属性中,在执行addInterceptors方法的时候<br> 实际上就是循环执行WebMvcConfigurer#addInterceptors(registry),所以我们可以通过实现WebMvcConfigurer<br> 接口重写addInterceptor就能添加拦截器了)<br> <font color="#0076b3">addInterceptors(registry);</font><br>
添加拦截器示例(参考方式一)
添加自定义拦截器实现原理
3. 添加内置 ConversionServiceExposingInterceptor 过滤器,在执行拦截器的preHandle方法时会为request添加一个<br> "org.springframework.core.convert.ConversionService"属性,值为mvcConversionService()返回值<br> <font color="#0076b3"> registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));</font><br>
4. 添加内置 ResourceUrlProviderExposingInterceptor 过滤器,在执行拦截器的preHandle方法时会为request添加一个<br> "org.springframework.web.servlet.resource.ResourceUrlProvider"属性,值为mvcResourceUrlProvider()返回值<br> <font color="#0076b3"> registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));</font><br>
5. 取出上面添加到过滤器,赋值给this.interceptors进行返回最终设置到mapping#interceptors中<br> <font color="#0076b3">this.interceptors = registry.getInterceptors();</font><br>
3. 在./2 实例化完后会执行该 bean的初始化操作,由于该bean实现了ApplicationContextAware接口<br> 所以会执行ApplicationObjectSupport#setApplicationContext方法(参考"doGetBean源码分析#4.7.1.4.5.8.2")<br>
1. 为applicationContext赋值<br><font color="#0076b3"> this.applicationContext = context;</font>
2. 为messageSourceAccessor 赋值<br> <font color="#0076b3">this.messageSourceAccessor = new MessageSourceAccessor(context);</font><br>
3. 执行子类WebApplicationObjectSupport#initApplicationContext方法<br> 最终会执行AbstractHandlerMapping#initApplicationContext方法<br> <font color="#0076b3">initApplicationContext(context);</font><br>
1. 供子类扩展的方法,这里的this.interceptors就是#1.2.4.1.2.2.1.2中添加的那些拦截器<br> <font color="#0076b3"> extendInterceptors(this.interceptors);</font><br>
2. 从bean容器中取出MappedInterceptor类型的bean放入添加到this.adaptedInterceptors中<br> 所以通过直接在bean容器中注册MappedInterceptor类型bean的方式也是可以添加拦截器的<br> <font color="#0076b3">detectMappedInterceptors(this.adaptedInterceptors);</font><br>
添加MappedInterceptor方式注册拦截器示例(参考方式二)
3. 将this.adaptedInterceptors中的那些拦截器适配后添加到this.adaptedInterceptors中<br> <font color="#0076b3"> initInterceptors();</font><br>
4. 在执行完./3后由于该bean又实现了InitializingBean接口,所以接着会执行<br> RequestMappingHandlerMapping#afterPropertiesSet(参考"doGetBean源码分析#4.7.1.4.5.8.3")<br>
1. 实例化一个BuilderConfiguration对象赋值给this.config <br><font color="#0076b3"> this.config = new RequestMappingInfo.BuilderConfiguration();</font><br>
2. 设置urlPathHelper默认为UrlPathHelper.class<br> <font color="#0076b3">this.config.setUrlPathHelper(getUrlPathHelper());</font><br>
3. 默认为AntPathMatcher,路径匹配校验器<br><font color="#0076b3"> this.config.setPathMatcher(getPathMatcher());</font><br>
3. 是否支持后缀补充,默认为true(比如url为/api/user.do的/api/user.html也能匹配上)<br> <font color="#0076b3"> this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);</font><br>
4. 是否添加"/"后缀,默认为true(比如url为/api/user的/api/user/也能匹配上)<br> <font color="#0076b3">this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);</font><br>
5. 是否采用mediaType匹配模式,比如.json/.xml模式的匹配,默认为false <br> <font color="#0076b3"> this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);</font><br>
6. mediaType处理类:ContentNegotiationManager<br> <font color="#0076b3"> this.config.setContentNegotiationManager(getContentNegotiationManager());</font><br>
7. 调用父类的方法<br> <font color="#0076b3"> super.afterPropertiesSet();</font>
1. 执行请求处理方法的初始化,会取出容器中所有的bean进行循环调用<br> processCandidateBean方法,需要注意的是只会从当前容器所有bean<br> 默认不会取父容器的bean(可通过detectHandlerMethodsInAncestorContexts参数控制)<br> <font color="#0076b3">processCandidateBean(beanName);</font><br>
1. 通过bean名称取出bean的类型<br> <font color="#0076b3">beanType = obtainApplicationContext().getType(beanName);</font>
2. 如果beanType不为null且该类有标注@Controller或@RequestMapping注解<br> 则进行检查请求处理方法<br> <font color="#0076b3"> detectHandlerMethods(beanName);</font>
1. 如果传入的是字符串类型则说明是beanName,则通过名称取出它的类型<br> 如果是个对象则直接取出对象的类型<br> <font color="#0076b3"> Class<?> handlerType = (handler instanceof String ?<br> obtainApplicationContext().getType((String) handler) : handler.getClass());</font>
handlerType不为null
2. 解析这个类中所标注了@RequestMapping注解的自定义方法放入一个Map中<br> key为Method,值为通过@RequestMapping信息创建的一个RequestMappingInfo对象<br><font color="#0076b3"> Map<Method, T> methods = MethodIntrospector.selectMethods(userType,method->{...})</font><br>
3. 循环上面解析出来的Map,将值注册到MappingRegistry中<br> handler此处为bean名称,invocableMethod为可执行的方法,mapping为RequestMappingInfo对象<br> <font color="#0076b3">registerHandlerMethod(handler, invocableMethod, mapping);</font><br>
registerHandlerMethod中会调用<br><font color="#0076b3">this.mappingRegistry.register(mapping, handler, method);</font><br>
1. 通过handler, method新建一个HandlerMethod对象<br> <font color="#0076b3">HandlerMethod handlerMethod = createHandlerMethod(handler, method);</font>
2. 判断此次添加的mapping是不是之前就有添加过对应的handlerMethod映射了<br> 如果有了而且和这次新创建的handlerMethod不同则抛出异常<br> <font color="#0076b3"> assertUniqueMethodMapping(handlerMethod, mapping);</font><br>
3. 将mapping,与handlerMethod的映射添加到mappingLookup中<br> <font color="#0076b3"> this.mappingLookup.put(mapping, handlerMethod);</font>
4. 添加url与mapping的映射添加到urlLookup中<br> <font color="#0076b3">this.urlLookup.add(url, mapping);</font>
比如/index这个请求url可能对应有Get方法的@RequestMapping和Post方法的@RequestMapping<br>
5. 保存name和handlerMethod的映射关系<br> <font color="#0076b3"> addMappingName(name, handlerMethod);</font>
<font color="#5c5c5c">6. 添加mapping与MappingRegistration对象的关系</font><font color="#0076b3"><br>this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));</font><br>
3. beanNameHandlerMapping(BeanNameUrlHandlerMapping)<br>
4. resourceHandlerMapping(SimpleUrlHandlerMapping)<br> 这个主要是用来匹配静态资源用的,在RequestMappingHandlerMapping没有匹配到<br> 符合要求的Handler的时候会进入到这个HandlerMapping里面进行匹配,它里面匹配出来的<br> 是HttpRequestHandler类型的Handler,与其对应的是HttpRequestHandlerAdapter适配器
5. welcomePageHandlerMapping(WelcomePageHandlerMapping)
2. 如果this.detectAllHandlerMappings为false,表示不去检测所有的HandlerMapping<br> 直接从bean容器中取名称为handlerMapping的bean添加到this.handlerMappings中<br>
3. 如果上面没有取到HandlerMapping则使用默认策略提供的HandlerMapping
像在非springboot项目的普通spring mvc项目中容器中是没有相关bean的<br>会使用策略<br>
5. 初始化请求处理适配器<br> <font color="#0076b3"> initHandlerAdapters(context);</font><br>
和../4逻辑一致,只不过是取出HandlerAdapter类型的bean<br>放在this.handlerAdapters中,包含右侧3个bean<br>
1. RequestMappingHandlerAdapter<br> 所有MethodHandler类型的Handler都会匹配这个适配器
1. 参考#1.2.4.1.2.1中注册requestMappingHandlerMapping bean<br> 会注册requestMappingHandlerAdapter<br>
2. 和参考#1.2.4.1.2.2类似,bean实例化的时候会执行<br> WebMvcConfigurationSupport#requestMappingHandlerAdapter方法<br>
1. 实例化一个RequestMappingHandlerAdapter 对象,主要添加了4个消息转换器<br> 不过在./3中会被重新赋值<br> <font color="#0076b3"> RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();</font><br>
1. <font color="#0076b3">this.messageConverters.add(new ByteArrayHttpMessageConverter());</font><br>
2. <font color="#0076b3">this.messageConverters.add(stringHttpMessageConverter);</font>
3.<font color="#0076b3"> this.messageConverters.add(new SourceHttpMessageConverter<>());</font>
4. <font color="#0076b3">this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());</font>
2. 设置内容协商管理器<br><font color="#0076b3"> adapter.setContentNegotiationManager(mvcContentNegotiationManager());</font><br>
3. 为adapter设置消息转换器<br> <font color="#0076b3"> adapter.setMessageConverters(getMessageConverters());</font><br>
1. 获取messageConverters<br> <font color="#0076b3"> getMessageConverters()</font>
如果getMessageConverters()为null
1. <font color="#0076b3">this.messageConverters = new ArrayList<>();</font>
2. 该方最终会从调用WebMvcConfigurer配置类中的configureMessageConverters方法进行自定义设置<br> 不过需要注意的是springboot中默认注册了一个WebMvcConfigurer配置WebMvcAutoConfigurationAdapter<br> 这个配置类中重写了configureMessageConverters方法,该方法会去bean容器中查找HttpMessageConverters类型的bean<br> HttpMessageConvertersAutoConfiguration类中有定义这个类型的bean,在实例化这个bean的时候最终会执行./3中的方法<br> 添加默认的MessageConverters,具体可查看HttpMessageConverters的构造<br> <font color="#0076b3"> configureMessageConverters(this.messageConverters);</font><br>
3. 如果this.messageConverters仍是null,则设置默认值,在./2中实例化HttpMessageConverters时会执行该方法<br> 会加载右侧10个messageConverter<br> <font color="#0076b3">addDefaultHttpMessageConverters(this.messageConverters);</font><br>
ByteArrayHttpMessageConverter<br>
StringHttpMessageConverter<br>
StringHttpMessageConverter<br>
ResourceHttpMessageConverter<br>
ResourceRegionHttpMessageConverter<br>
SourceHttpMessageConverter<br>
AllEncompassingFormHttpMessageConverter<br>
MappingJackson2HttpMessageConverter<br>
MappingJackson2HttpMessageConverter<br>
Jaxb2RootElementHttpMessageConverter
4. 供用户通过WebMvcConfigurer配置自定义messageConverter的扩展接口<br> <font color="#0076b3">extendMessageConverters(this.messageConverters);</font>
5. 返回this.messageConverters,会被设置给adapter#messageConverters<br><font color="#0076b3"> retrun this.messageConverters;</font>
2. 将上面获取的messageConverters设置给adapter#messageConverters<br><font color="#0076b3"> this.messageConverters = messageConverters;</font>
4. 为adapter设置webBindingInitializer, 在#2.3.5.4.11.4.2创建WebDataBinderFactory对象时会设置该webBindingInitializer属性 <br> 这个初始化器中设置了一些数据绑定的全局设置,在对请求数据进行绑定的时候会起作用<br> <font color="#0076b3">adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());</font><br>
先会去bean容器中查找是否有ConfigurableWebBindingInitializer类型的bean<br>如果没有找到则实例化一个ConfigurableWebBindingInitializer对象(默认的实例化的时候会设置ConversionService为WebConversionService)<br>
5. 为adapter设置用户自定义参数解析器(通过WebMvcConfigurer配置自定义)<br> <font color="#0076b3">adapter.setCustomArgumentResolvers(getArgumentResolvers());</font><br>
6. 为adapter设置用户自定义返回值处理器(通过WebMvcConfigurer配置自定义)<br> <font color="#0076b3">adapter.setCustomReturnValueHandlers(getReturnValueHandlers());</font><br>
如果jackson2Present的值为true<br> (在WebMvcConfigurationSupport的静态代码块中会通过判断是否有引入jackson2的包来设置此值)<br>
7. <font color="#0076b3">adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));</font>
8.<font color="#0076b3"> adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));</font><br>
9. spring mvc异步支持相关设置<br>
3. 实例化完初始化的时候由于bean实现了ApplicationContextAware接口接口<br> 所以会调用setApplicationContext方法为applicationContext赋值,这一个只是普通的赋值操作<br> 不像requestMappingHandlerMapping做了一些其他的事情
4. 在执行完./3后由于该bean又实现了InitializingBean接口,所以接着会执行<br> RequestMappingHandlerAdapter#afterPropertiesSet方法<br>
1. 初始化ControllerAdvice缓存<br> <font color="#0076b3"> initControllerAdviceCache();</font><br>
1. 查找bean容器中有标注@ControllerAdvice注解的bean构造成ControllerAdviceBean对象<br> <font color="#0076b3">List<ControllerAdviceBean> adviceBeans =<br> ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());</font><br>
2. 定义一个List用来存放上面adviceBeans 中那些RequestBodyAdvice或ResponseBodyAdvice类型的bean<br> <font color="#0076b3">List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();</font><br>
循环./1中 adviceBeans <br>
3. 查找当前bean中没有标注@RequestMapping注解但标注了@ModelAttribute<br> 注解的方法放到modelAttributeAdviceCache缓存中<br> <font color="#0076b3">this.modelAttributeAdviceCache.put(adviceBean, attrMethods);</font><br>
4. 查找当前bean中标注了@InitBinder注解的方法添加到initBinderAdviceCache缓存中<br> <font color="#0076b3"> this.initBinderAdviceCache.put(adviceBean, binderMethods);</font><br>
5. 如果当前bean是RequestBodyAdvice或ResponseBodyAdvice类型则添加到./2 中的requestResponseBodyAdviceBeans <br>
6. 如果./2中的requestResponseBodyAdviceBeans不为空<br> 则将requestResponseBodyAdviceBeans添加到RequestMappingHandlerAdapter#requestResponseBodyAdvice列表的最前面<br>
如果this.argumentResolvers为null,则添加默认值<br>
2. 获取默认值
1. <font color="#0076b3">List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();</font>
2. 添加基于注解的默认参数解析器,共15个<br>
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));<br> resolvers.add(new RequestParamMapMethodArgumentResolver());<br> resolvers.add(new PathVariableMethodArgumentResolver());<br> resolvers.add(new PathVariableMapMethodArgumentResolver());<br> resolvers.add(new MatrixVariableMethodArgumentResolver());<br> resolvers.add(new MatrixVariableMapMethodArgumentResolver());<br> resolvers.add(new ServletModelAttributeMethodProcessor(false));<br> resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));<br> resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));<br> resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));<br> resolvers.add(new RequestHeaderMapMethodArgumentResolver());<br> resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));<br> resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));<br> resolvers.add(new SessionAttributeMethodArgumentResolver());<br> resolvers.add(new RequestAttributeMethodArgumentResolver());
3. 添加基于类型的默认参数解析器,共9个<br>
resolvers.add(new ServletRequestMethodArgumentResolver());<br> resolvers.add(new ServletResponseMethodArgumentResolver());<br> resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));<br> resolvers.add(new RedirectAttributesMethodArgumentResolver());<br> resolvers.add(new ModelMethodProcessor());<br> resolvers.add(new MapMethodProcessor());<br> resolvers.add(new ErrorsMethodArgumentResolver());<br> resolvers.add(new SessionStatusMethodArgumentResolver());<br> resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
4. 添加#1.2.5.1.2.5用户自定义的那些参数解析器<br><font color="#0076b3"> resolvers.addAll(getCustomArgumentResolvers());</font>
5. <font color="#0076b3">resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));</font><br>
6. <font color="#0076b3">resolvers.add(new ServletModelAttributeMethodProcessor(true));<br></font> ServletModelAttributeMethodProcessor这个解析器在./2中添加过了,不过构造参数是false<br> 此处又重新添加了个构造为true的,主要是为annotationNotRequired属性赋值为true,这样那些我们自定义的实体类最终也能被这个解析器处理<br> 这个解析器放在最后面,在上面的那些解析器都解析不了的时候做兜底用的
7.<font color="#0076b3"> return resolvers;</font>
3. <font color="#0076b3">this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);</font>
如果this.initBinderArgumentResolvers为null,则添加默认值<br>
4. 获取默认值<br>
1. <font color="#0076b3">List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();</font>
2. 添加基于注解的默认参数解析器,共9个<br>
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));<br> resolvers.add(new RequestParamMapMethodArgumentResolver());<br> resolvers.add(new PathVariableMethodArgumentResolver());<br> resolvers.add(new PathVariableMapMethodArgumentResolver());<br> resolvers.add(new MatrixVariableMethodArgumentResolver());<br> resolvers.add(new MatrixVariableMapMethodArgumentResolver());<br> resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));<br> resolvers.add(new SessionAttributeMethodArgumentResolver());<br> resolvers.add(new RequestAttributeMethodArgumentResolver()); <br>
3. 添加基于类型的默认参数解析器
resolvers.add(new ServletRequestMethodArgumentResolver());<br> resolvers.add(new ServletResponseMethodArgumentResolver());<br>
4. 添加#1.2.5.1.2.5用户自定义的那些参数解析器<br> <font color="#0076b3">resolvers.addAll(getCustomArgumentResolvers());</font><br>
5. <font color="#0076b3">resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));</font>
6. <font color="#0076b3">return resolvers;</font>
5. <font color="#0076b3">this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);</font>
如果this.returnValueHandlers为null,则添加默认值
6. 获取默认值
1. <font color="#0076b3">List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();</font><br>
2. 单个返回值处理相关,共10个<br>
ModelAndViewMethodReturnValueHandler<br>
ModelMethodProcessor<br>
ViewMethodReturnValueHandler<br>
ResponseBodyEmitterReturnValueHandler<br>
StreamingResponseBodyReturnValueHandler<br>
HttpEntityMethodProcessor<br>
HttpHeadersReturnValueHandler<br>
CallableMethodReturnValueHandler<br>
DeferredResultMethodReturnValueHandler<br>
AsyncTaskMethodReturnValueHandler
3. 基于注解的返回值处理相关,共2个
ModelAttributeMethodProcessor<br>
RequestResponseBodyMethodProcessor<br>
4. 多个返回值处理相关,共2个
ViewNameMethodReturnValueHandler<br>
MapMethodProcessor
5. 添加#1.2.5.1.2.6中用户自定义的那些处理器<br> handlers.addAll(getCustomReturnValueHandlers());<br>
6. 如果this.modelAndViewResolvers为空<br> <font color="#0076b3">handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));</font><br>
7. 如果this.modelAndViewResolvers不为空<br><font color="#0076b3"> handlers.add(new ModelAttributeMethodProcessor(true));</font>
8. <font color="#0076b3">return handlers;</font>
7. <font color="#0076b3">this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);</font>
2. HttpRequestHandlerAdapter<br> 所有HttpRequestHandler类型的Handler会匹配该处理器<br> 比如resourceHandlerMapping中匹配出的Handler
3. SimpleControllerHandlerAdapter
6. 初始化异常处理<br> <font color="#0076b3"> initHandlerExceptionResolvers(context);</font><br>
和./4逻辑一致,只不过是取出HandlerExceptionResolver类型的bean放在this.handlerExceptionResolvers中,包含右侧2个bean
DefaultErrorAttributes<br>
HandlerExceptionResolverComposite<br>
7. 初始化请求到ViewName的转译,通过ViewName可来找到对应的视图<br> <font color="#0076b3"> initRequestToViewNameTranslator(context);</font><br>
1. 从bean容器中去查找RequestToViewNameTranslator类型的bean赋值给this.viewNameTranslator
2. 上面取不到抛出异常会使用默认策略,默认会使用DefaultRequestToViewNameTranslator
8. 初始化视图解析<br> <font color="#0076b3"> initViewResolvers(context);</font><br>
和./4逻辑一致,只不过是取出ViewResolver类型的bean放在this.viewResolvers中,包含右侧4个bean<br>
BeanNameViewResolver<br>
ViewResolverComposite<br>
InternalResourceViewResolver<br>
ContentNegotiatingViewResolver
9. 初始化FlashMap管理,FlashMap主要用在redirect中传递参数<br> <font color="#0076b3"> initFlashMapManager(context);</font><br>
1. 从bean容器中去查找FlashMapManager类型的bean赋值给flashMapManager
2. 上面取不到抛出异常会使用默认策略,默认会使用SessionFlashMapManager
2. 容器初始化后对请求进行处理<br>
1. 请求会进入ApplicationFilterChain#doFilter先执行过滤器,然后才会执行DispatcherServlet#service方法<br>
ApplicationFilterChain模拟代码示例
2. 调用DispatcherServlet#service会进入FrameworkServlet#service方法<br>
1. 从request中获取请求的方法类型,然后通过方法类型获取对应的HttpMethod枚举值<br> <font color="#0076b3"> HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());</font>
2. 如果上一步取到的httpMethod是PATCH值或为null,则直接调用<br> FrameworkServlet#processRequest方法进行处理了<br> 这一步主要是处理了Servlet不支持PATCH请求类型的情况<br>
3. 调用HTTPServlet#service方法判断到底调用那种类型的方法(如get请求调用doGet,post请求调用doPost)<br> 然后FrameworkServlet类重写了doGet,doPost这些方法,方法里面最终都是调用processRequest(request, response);<br> <font color="#0076b3">super.service(request, response);</font><br>
3. 经过./2后请求最终都会进入FrameworkServlet#processRequest方法
1. 先从线程上下文localeContextHolder中取LocaleContext(主要是供finally中重置线程上下文使用)<br> 然后通过request.getLocale()构建一个SimpleLocaleContext类型的LocaleContext对象<br> <font color="#0076b3"> LocaleContext localeContext = buildLocaleContext(request);</font>
2. 先从线程上下文localeContextHolder中取RequestAttributes (主要是供finally中重置线程上下文使用)<br> 然后通过request和response构建一个ServletRequestAttributes对象(如原先线程上下文中的RequestAttributes不是ServletRequestAttributes类型则返回null) <br> <font color="#0076b3"> ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);</font>
3. 异步管理器相关的东西(后续再分析,先占个位)<br>
4. 将./1和./2中构建出的对象放入到各自的线程上下文中<br> <font color="#0076b3"> initContextHolders(request, localeContext, requestAttributes);</font>
5. 执行DispatcherServlet#doService方法<br> <font color="#0076b3">doService(request, response);</font><br>
1. 如果是request属性中包含“javax.servlet.include.request_uri”属性(即<jsp:incluede page="xxx.jsp"/>的情况)<br> 则将request中的“org.springframework.web.servlet”开头的属性存在一个Map快照中 attributesSnapshot<br> 在finally块中如果发现request中的属性值和快照中的属性值不同会重新设置回快照中的属性值<br> <font color="#0076b3"> attributesSnapshot.put(attrName, request.getAttribute(attrName));</font><br>
2. 设置一些常用的属性到request的属性中方便后面获取,主要包括<br>
CONTEXT<br>
LOCALE_RESOLVER<br>
THEME_RESOLVER<br>
THEME_SOURCE
3. 如果是重定向请求,还会额外设置一些重定向相关的属性到request<br>
INPUT_FLASH_MAP
OUTPUT_FLASH_MAP<br>
FLASH_MAP_MANAGER
4. 执行 doDispatch方法<br> doDispatch(request, response);
1. <font color="#0076b3">HttpServletRequest processedRequest = request;</font>
2.<font color="#0076b3"> HandlerExecutionChain mappedHandler = null;</font>
3. 取出异步管理器<br><font color="#0076b3"> WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);</font>
4. <font color="#0076b3">ModelAndView mv = null;</font>
5. 检查是不是文件上传,如果是的话会通过#1.2.1中初始化的StandardServletMultipartResolver#resolveMultipart方法<br> 将request包装为一个StandardMultipartHttpServletRequest对象赋值给processedRequest <br> 这里需要注意的是由于对processedRequest 重新赋值了,所以后续方法中无法再从RequestContextHolder中获取之前设置的属性<br> <font color="#0076b3">processedRequest = checkMultipart(request);</font><br>
6. 获取当前请求的处理程序执行链HandlerExecutionChain <br> <font color="#0076b3">mappedHandler = getHandler(processedRequest);</font><br>
此方法中会循环#1.2.4.1中初始化的那个几个HandlerMapping<br>如果某个HandlerMapping#getHandler方法有取到值则直接return<br>这里我们只分析最常用的RequestMappingHandlerMapping<br>需要注意的是在RequestMappingHandlerMapping没有匹配到Handler的时候<br>会继续执行后面的HandlerMapping,在执行到resourceHandlerMapping的时候一般是能匹配到的<br>因为它里面有个一条匹配规则是/**,在./8中会匹配到HttpRequestHandlerAdapter适配器进行处理
1. 通过请求获取匹配的处理器<br><font color="#0076b3"> Object handler = getHandlerInternal(request);</font><br>
1. 取出request中的uri地址<br><font color="#0076b3"> String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);</font>
2. 通过lookupPath去查找最终执行的处理器<br> <font color="#0076b3">HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);</font><br>
1. <font color="#0076b3">List<Match> matches = new ArrayList<>();</font>
2. 从缓存中取出该lookupPath对应的RequestMappingInfo对象(#1.2.4.1.2.4.7.1.2.3.4有添加所有url和RequestMappingInfo的映射)<br> 由于一个uri可能对应有多个请求类型的(如:Get,Post)@RequestMapping<br> 所以返回的是一个RequestMappingInfo类型的List<br> <font color="#0076b3">List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);</font><br>
3. 如果./2获取的directPathMatches不为null,则进一步通过RequestMappingInfo 中的method、headers、consumes这些值<br> 从directPathMatches中过滤出匹配的RequestMappingInfo放入到matches中<br> <font color="#0076b3"> addMatchingMappings(directPathMatches, matches, request);</font><br>
4. 如果此时matches仍旧为空,则尝试通过request去匹配缓存中所有的RequestMappingInfo,看能不能找到匹配的<br> <font color="#0076b3">addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);</font><br>
如果到这一步matches不为空<br>
5. 先按method,patterns,params等优先级对matches进行排序处理<br>
6. 取出第一个matches中的第一个,也就是最佳匹配<br><font color="#0076b3"> Match bestMatch = matches.get(0);</font>
7. 如果matches中的个数大于1的话把matches中的第二个也就是次最佳匹配也取出来<br> 如果bestMatch和secondBestMatch用比较器比较是相等则抛出IllegalStateException异常<br> <font color="#0076b3">Match secondBestMatch = matches.get(1);</font><br>
8. 走到这一步说明没啥问题则将最佳匹配的HandlerMetod放入request的"HandlerMapping.class.getName() + '.bestMatchingHandler'"属性中<br> <font color="#0076b3">request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);</font>
9. 接着又执行了如下方法,在request中设置了一些其他属性<br><font color="#0076b3"> handleMatch(bestMatch.mapping, lookupPath, request);</font><br>
10. 最终返回HandlerMethod<br><font color="#0076b3"> return bestMatch.handlerMethod;</font>
11. matches为空,则调用handleNoMatch方法进行处理,这是一个可供子类重写的方法<br> RequestMappingInfoHandlerMapping有重写该方法进行部分匹配,如果还是没有匹配到则返回null<br> <font color="#0076b3"> return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);</font><br>
3. 如果取到的handlerMethod不为null,则判断handlerMethod中的bean属性是不是String类型<br> 如果是的话通过该值作为bean名称去容器中查找对应的bean对象赋值给该bean属性<br> <font color="#0076b3">return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);</font><br>
2. 如果./1没有取到就去取默认AbstractHandlerMapping#defaultHandler值<br> <font color="#0076b3"> handler = getDefaultHandler();</font><br>
3. 如果到这一步还没取到值就return null了
4. 如果取到的handler值为String类型,则将该handler值作为bean名称去容器中获取对应的bean赋值给handler<br> (不明白什么时候取出的是一个String,一般是一个HandlerMethod对象)<br>
5. 将handler包装成一个HandlerExecutionChain对象,里面会包含匹配的拦截器<br><font color="#0076b3"> HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);</font><br>
1. 如果handler本来就是HandlerExecutionChain类型则直接强制转换就好了<br> 如果handler不是则通过handler构建一个HandlerExecutionChain<br> <font color="#0076b3"> HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?<br> (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));</font>
2. 为上面的chain对象添加匹配的匹配的拦截器(在#1.2.4.1.2.3.3中有将过滤器添加到AbstractHandlerMapping#adaptedInterceptors中)<br> 需要注意的是MappedInterceptor类型的拦截器只有和当前请求匹配才会添加到HandlerExecutionChain#interceptorList中<br> 而非MappedInterceptor类型的会直接添加到HandlerExecutionChain#interceptorList中
在#1.2.4.1.2.2.1.2的时候有添加两个默认的非MappedInterceptor类型的拦截器
ConversionServiceExposingInterceptor
ResourceUrlProviderExposingInterceptor
3. <font color="#0076b3">return chain;</font><br>
6. 对CORS跨域资源共享的支持
7. <font color="#0076b3">return executionChain;</font>
7. 如果mappedHandler为null,则判断this.throwExceptionIfNoHandlerFound是否为true<br> 为true的话抛出NoHandlerFoundException异常,否则设置response状态为404然后return请求<br><font color="#0076b3"> noHandlerFound(processedRequest, response);</font><br>
8. 获取匹配的适配器<br> <font color="#0076b3">HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());</font><br>
会循环#1.2.5中初始化的那几个适配器,如果mappedHandler.getHandler()<br> 中取出的处理器是HandlerMethod类型则会交给 RequestMappingHandlerAdapter 处理
9. 如果是Get请求,且请求的内容没有变化,则直接返回
10. 执行拦截器的前置方法<br><font color="#0076b3"> if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }</font>
1. 将../6.5.2中添加到HandlerExecutionChain#interceptorList中的那些拦截器<br> 转为数组放入HandlerExecutionChain#interceptors中进行返回<br> <font color="#0076b3"> HandlerInterceptor[] interceptors = getInterceptors();</font><br>
如果上面的interceptors不为空则循环这些拦截器<br>此处需要注意的是有两个默认的拦截器会被执行<br>ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor<br>他们会为request设置两个属性,详情查看#1.2.4.1.2.2.1.2
2. 如果当前拦截器返回的preHandle方法返回false则先在循环执行它前面那些拦截器的<br> afterCompletion方法后会return false整个方法<br>
3. 如果preHandle返回true则执行下一个拦截器的preHandle
11. 执行适配器的handle方法(此处只分析最常用的RequestMappingHandlerAdapter)<br> <font color="#0076b3">mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</font>
1. <font color="#0076b3">ModelAndView mav;</font>
2. 对请求进行检查,如检查是否是自己支持的请求方法类型<br> <font color="#0076b3"> checkRequest(request);</font><br>
3. 判断同一个session是否需要串行执行(this.synchronizeOnSession为true)<br> 需要的话会取出session,如果session不为null会对它加同步锁然后再调用invokeHandlerMethod方法<br>
4. 如果同一个session不需要串行则直接执行invokeHandlerMethod<br><font color="#0076b3"> mav = invokeHandlerMethod(request, response, handlerMethod);</font>
1. <font color="#0076b3">ServletWebRequest webRequest = new ServletWebRequest(request, response);</font>
2. 处理@InitBinder注解的方法<br> <font color="#0076b3"> WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);</font><br>
1. 获取处理处理器的bean类型<br> <font color="#0076b3">Class<?> handlerType = handlerMethod.getBeanType();</font><br>
2. 先尝试通过handlerType从缓存中获取@InitBinder标注的方法<br> <font color="#0076b3"> Set<Method> methods = this.initBinderCache.get(handlerType);</font>
如果缓存中没有就去解析<br>
3. 解析handlerType类中被@InitBinder标注的方法<br> <font color="#0076b3">methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);</font><br>
4. 存入缓存中下次进来就直接从缓存中获取<br> <font color="#0076b3"> this.initBinderCache.put(handlerType, methods);</font>
5. <font color="#0076b3">List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();</font><br>
6. 循环#1.2.5.1.4.1.4中那些有@InitBinder方法的ControllerAdviceBean<br>
如果handlerType满足ControllerAdviceBean的应用条件(一般没有特别设置是应用到所有类型的)<br> 则将当前ControllerAdviceBean中标注有@InitBinder的那些方法包装成InvocableHandlerMethod对象<br>添加到initBinderMethods中<br>
7. ./3 中解析出的那些方法也包装成InvocableHandlerMethod对象后添加到initBinderMethods中<br>
8. 最后通过initBinderMethods构造一个ServletRequestDataBinderFactory对象<br> <font color="#0076b3"> return createDataBinderFactory(initBinderMethods);</font>
3. 处理@ModelAttribute标注的方法,需要注意的先添加的是ControllerAdviceBean中那些@ModelAttribute方法<br> 所以ControllerAdviceBean中的@InitBinder方法优先及更高,在./11.3中可以看出属性名相同时后面的属性是不会再<br> 添加到mavContainer中的<br> <font color="#0076b3">ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);</font><br>
1. 通过handlerMethod获取SessionAttributesHandler,如果该处理器类有标注SessionAttributes注解<br> 会解析取出注解中names属性的值放入SessionAttributesHandler#knownAttributeNames中<br> knownAttributeNames中包含的属性名称对应的属性会被缓存到session中<br><font color="#0076b3"> SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);</font>
2. 接下来的那些操作和../2 中的处理基本相同,只是由@InitBinder注解换成了@ModelAttribute注解
3. 最后构建一个 ModelFactory对象<br> <font color="#0076b3"> return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);</font>
4. 将handlerMethod包装成ServletInvocableHandlerMethod<br> <font color="#0076b3"> ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);</font><br>
5. 为invocableMethod设置参数解析器(#1.2.5.1.4.2有设置)<br> <font color="#0076b3"> invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);</font><br>
6. 为invocableMethod设置返回值处理器(#1.2.5.1.4.6有设置)<br> <font color="#0076b3"> invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);</font><br>
7. 设置数据绑定工厂为./2中创建的对象<br> <font color="#0076b3"> invocableMethod.setDataBinderFactory(binderFactory);</font><br>
8. 设置用于获取参数名的对象<br> <font color="#0076b3">invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);</font><br>
9. 实例化一个模型视图容器<br> <font color="#0076b3">ModelAndViewContainer mavContainer = new ModelAndViewContainer();</font><br>
10. FlashMap数据保存到Model中<br> <font color="#0076b3">mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));</font><br>
11. 初始化./3中的modelFactory对象<br> <font color="#0076b3"> modelFactory.initModel(webRequest, mavContainer, invocableMethod);</font><br>
1. 从获取session中获取knownAttributeNames中包含的那些属性(../3.1中有设置)<br> <font color="#0076b3">Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);</font><br>
2. 如果sessionAttributes中的属性在container中不存在则添加到 container中<br> container即上一步传入的mavContainer,它里面有个ModelMap defaultModel属性会保存添加的属性<br> <font color="#0076b3"> container.mergeAttributes(sessionAttributes);</font><br>
3. 循环执行那些@ModelAttribute方法,会将方法的返回值作为属性添加到container中<br> <font color="#0076b3">invokeModelAttributeMethods(request, container);</font><br>
4. 这一步会去handlerMethod 中找出那些入参上标注了ModelAttribute注解的参数<br> 如果该参数在SessionAttributes注解的配置中(attributeNames或attributeTypes匹配)<br> 会添加到knownAttributeNames中,添加到knownAttributeNames后还会判断该属性是否存在container中<br> 不存在的话会冲session中取出该属性值添加到container中,如果从session中取不到值则会抛出HttpSessionRequiredException异常<br>
12. 异步相关的一些东西,暂时不管<br>
13. 调用处理器的方法<br> <font color="#0076b3"> invocableMethod.invokeAndHandle(webRequest, mavContainer);</font><br>
1. 调用父类InvocableHandlerMethod#invokeForRequest方法执行处理器方法的调用<br><font color="#0076b3"> Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);</font><br>
1. 获取处理器方法的实参数组<br> <font color="#0076b3">Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</font>
1. 如果处理器方法参数为空,则返回一个空的对象数组作为实参<br>
2. <font color="#0076b3">MethodParameter[] parameters = getMethodParameters();</font>
3. 定义一个args对象数组,用于存储转化后的实参列表<br><font color="#0076b3"> Object[] args = new Object[parameters.length]</font><br>
循环 parameters
4. <font color="#0076b3">MethodParameter parameter = parameters[i];</font>
5. <font color="#0076b3">parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);</font><br>
6. 如果可变参数providedArgs中包含当前形参parameter类型的对象,则使用该对象作为实参,继续循环处理下一个形参<font color="#0076b3"><br> args[i] = findProvidedArgument(parameter, providedArgs);</font><br>
7. 判断this.resolvers(#2.3.5.4.11.4.5中设置的HandlerMethodArgumentResolverComposite对象)中是否有支持当前形参parameter的参数解析器<br> 没有则抛出IllegalStateException异常<br> <font color="#0076b3"> this.resolvers.supportsParameter(parameter)</font><br>
8. 通过HandlerMethodArgumentResolverComposite#resolveArgument进行解析当前形参parameter<br> <font color="#0076b3"> args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);</font><br>
1. 循环HandlerMethodArgumentResolverComposite#argumentResolvers中的那些HandlerMethodArgumentResolver<br> 通过HandlerMethodArgumentResolver#supportsParameter方法筛选出匹配的HandlerMethodArgumentResolver<br> <font color="#0076b3">HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);</font><br>
2. ./1中没找匹配的resolver则抛出IllegalArgumentException异常<br>
3. 调用匹配的那个HandlerMethodArgumentResolver#resolveArgument方法,将返回值作为实参数进行返回<br> 右侧分析最比较常用的方法解析器<br><font color="#0076b3"> return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);</font><br>
1. ServletModelAttributeMethodProcessor<br> 在#1.2.5.1.4.2.3的时候有添加一个该类型的默认的参数处理器<br> 构造时传入了true,表示处理那些没显式标注ModelAttribute注解的自定义类参数<br>
1. 获取参数的名称,如果当前要处理的形参有标注ModelAttribute注解则去注解的value值作为name<br> 否则通过 Conventions#getVariableNameForParameter方法获取参数名称,比如User 为user<br><font color="#0076b3"> String name = ModelFactory.getNameForParameter(parameter);</font><br>
2. 如果当前要处理的形参有标注ModelAttribute,则设置mavContainer中该name属性的绑定状态为绑定中<br> <font color="#0076b3">mavContainer.setBinding(name, ann.binding());</font><br>
3. <font color="#0076b3">Object attribute = null;</font>
4. <font color="#0076b3">BindingResult bindingResult = null;</font>
5. 如果mavContainer中包含该name的属性,则设置attribute 为该name的属性值<br> <font color="#0076b3"> attribute = mavContainer.getModel().get(name);</font>
6. 如果mavContainer中不包含该name的属性,则会通过反射实例化该参数类型的对象<br>
7. 通过binderFactory#createBinder方法创建一个WebDataBinder 对象(#2.3.5.4.11.4.7有设置binderFactory)<br> <font color="#0076b3">WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);</font>
1. 创建一个WebDataBinder实例<br> <font color="#0076b3">WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);</font>
2. 将初始化器中的一些全局配置设置到dataBinder中,比如设置conversionService(初始化器ConfigurableWebBindingInitializer的初始化查看#1.2.5.1.2.4)<br> 以及设置validator 为LocalValidatorFactoryBean(在../9中数据校验的时候会用到这个校验器)<br> <font color="#0076b3">this.initializer.initBinder(dataBinder, webRequest);</font><br>
3. 初始化dataBinder<br> <font color="#0076b3"> initBinder(dataBinder, webRequest);</font><br>
循环执行binderMethods中的那些方法<br>也就是在#2.3.5.4.11.4.2中收集的那些带@InitBinder注解的方法
1. 判断dataBinder中要处理的那个参数名称是否符合@InitBinder处理条件<br> @InitBinder中没有配置value表示处理任何名称的参数<br> <font color="#0076b3"> isBinderMethodApplicable(binderMethod, dataBinder)</font>
2. 调标注@InitBinder标注的方法,可以往dataBinder中注册一些属性编辑器,用来做些转化操作,比如CustomDateEditor<br> <font color="#0076b3">Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);</font><br>
3. 判断./2中是否有返回值,有返回值抛出异常,所以@InitBinder标注的方法是不允许有返回值的<br>
4. 返回dataBinder<br> <font color="#0076b3"> return dataBinder;</font>
8. 将请求中的参数绑定到attribute<br> <font color="#0076b3">bindRequestParameters(binder, webRequest);</font><br>
1. 将NativeWebRequest转为ServletRequest类型<br> <font color="#0076b3"> ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);</font>
2. 将binder强转为ServletRequestDataBinder类型<br> <font color="#0076b3"> ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;</font><br>
3. 执行绑定操作<br><font color="#0076b3"> servletBinder.bind(servletRequest);</font><br>
1. 将请求中的参数放入到MutablePropertyValues 对象中<br> <font color="#0076b3">MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);</font><br>
2. 从请求中取出文件上传MultipartRequest数据,如果不为空则添加到./1 mpvs中<br>
3. 这一步主要是从uri中取出占位符中的参数添加的 mpvs中(如xxx/xxx/{id}这种形式)<br> <font color="#0076b3"> addBindValues(mpvs, request);</font>
4. 将上面几步添加到mpvs中的数据绑定到目标对象中<br> <font color="#0076b3"> doBind(mpvs);</font>
1. 检查mpvs中是否包含默认参数,即参数名以!开头,比如!age。如果mpvs中没有参数名为age参数<br> 则会将!age去除!添加到mpvs中作为age参数<br> <font color="#0076b3">checkFieldDefaults(mpvs);</font><br>
2. 和./1中有些类似,会判断是否有参数名以_开头,做一些空值设置<br> <font color="#0076b3"> checkFieldMarkers(mpvs);</font><br>
3. 调用父类DataBinder绑定方法<br> <font color="#0076b3"> super.doBind(mpvs);</font><br>
1. 校验是否有不被允许的字段(可以通过给allowedFields设置来添加自己的允许字段)<br> <font color="#0076b3"> checkAllowedFields(mpvs);</font><br>
2. 校验必填字段是否有为空的<br> <font color="#0076b3"> checkRequiredFields(mpvs);</font>
3. <font color="#0076b3">applyPropertyValues(mpvs);</font><br>
1. 获取属性访问器即通过<br> (其实前面很多地方可能已经调用过这个方法进行获取这个访问器了,统一在这个地方分析)<br> <font color="#0076b3">getPropertyAccessor()</font><br>
1. 即获取bindingResult属性,第一次获取时会初始化bindingResult值<br> <font color="#0076b3"> getInternalBindingResult()</font>
1. 实例化一个BeanPropertyBindingResult 对象<br> <font color="#0076b3"> BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(),<br> getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());</font>
2. 如果this.conversionService不为空,则设置到result中<br> <font color="#0076b3"> result.initConversion(this.conversionService);</font><br>
3. 如果this.messageCodesResolver不为空,则设置到result中<br> <font color="#0076b3"> result.setMessageCodesResolver(this.messageCodesResolver);</font><br>
4. <font color="#0076b3">return result;</font>
2. 通过上一步的bindingResult获取属性访问器,其实就是通过<br> 工厂类PropertyAccessorFactory创建一个BeanWrapper对象<br> <font color="#0076b3"> getPropertyAccessor()</font><br>
1. 通过工厂类PropertyAccessorFactory创建BeanWrapper<br> <font color="#0076b3"> return PropertyAccessorFactory.forBeanPropertyAccess(this.target);</font>
2. 通过上面的属性访问器进行设置操作<br> <font color="#0076b3"> setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());</font>
1. 取出PropertyValue列表<br> <font color="#0076b3">List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?<br> ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));</font>
2. 循序propertyValues将PropertyValue设置到目标对象中<br> <font color="#0076b3">setPropertyValue(pv);</font>
最终在设值时涉及到的数据转换工作是会调用委托类TypeConverterDelegate#convertIfNecessary方法<br>会优先查找是否有合适的PropertyEditor,没有的话再ConversionService列表中查找是否有合适的。<br>如果还是没有则判断设置的值类型进行默认的转换操作<br>
ConversionService类关系图
一些数据绑定示例
9. 数据校验,验证绑定到attribute中的数据是否都是合法的,比如attribute对象中通过@NotNull声明某个字段是非空的<br> 而绑定后该字段仍为null,则会抛出BindException异常(注意当前参数的后一个参数为Errors类型时不会抛出异常,详情查看./12)<br> <font color="#0076b3">validateIfApplicable(binder, parameter);</font><br>
10. 处理attribute对象不是当前参数类型的情况<br>
11. 取出bindingResult<br> <font color="#0076b3"> bindingResult = binder.getBindingResult();</font>
12. 从bindingResult取出绑定后的属性添加到map中供后面添加到mavContainer中<br> 此处需要注意的是除了添加绑定后的目标对象,还将bindResulte对象本身也作为一个属性添加到了map中<br> 主要是用来处理后./9中数据不合法且后一个参数为Errors类型的情况,ErrorsMethodArgumentResolver参数处理器会将bindResulte对象设置到Errors参数中<br> <font color="#0076b3"> Map<String, Object> bindingResultModel = bindingResult.getModel();</font><br>
13. 先移除mavContainer中存在bindingResultModel中的那些属性<br><font color="#0076b3"> mavContainer.removeAttributes(bindingResultModel);</font><br>
14. 添加bindingResultModel中的属性到mavContainer中<br><font color="#0076b3"> mavContainer.addAllAttributes(bindingResultModel);</font><br>
15. <font color="#0076b3">return attribute;</font>
2. RequestResponseBodyMethodProcessor<br> 从处理器的supportsParameter方法可以看出该处理器<br> 能处理标注了@RequestBody注解的参数<br>
1. 处理参数是Optional的情况<br> <font color="#0076b3"> parameter = parameter.nestedIfOptional();</font>
2. 通过消息转换器将请求体中的数据转换为形参类型的实参对象<br> <font color="#0076b3">Object arg = readWithMessageConverters<br> (webRequest, parameter, parameter.getNestedGenericParameterType());</font><br>
1.<font color="#0076b3"> HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);</font>
2. <font color="#0076b3">ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);</font>
3. <font color="#0076b3">Object arg = readWithMessageConverters(inputMessage, parameter, paramType);</font>
1. 取出contentType<br> <font color="#0076b3"> contentType = inputMessage.getHeaders().getContentType();</font><br>
2. 如果contentType为null,则设置为application/octet-stream<br> <font color="#0076b3"> contentType = MediaType.APPLICATION_OCTET_STREAM;</font>
3. 定义一个对象用来存储请求body转换后的对象<br> <font color="#0076b3"> Object body = NO_VALUE;</font>
4. 将inputMessage包装为EmptyBodyCheckingHttpInputMessage对象<br> 内部会通过回退流PushbackInputStream判断body是不是空的<br> <font color="#0076b3"> message = new EmptyBodyCheckingHttpInputMessage(inputMessage);</font><br>
循环查找匹配的HttpMessageConverter(#1.2.5.1.2.3.1.3由设置)<br>
5. 取出当前转换器的类型<br> <font color="#0076b3">Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();</font>
6. 将当前转换器强转为GenericHttpMessageConverter类型,如果不是该类型则赋值为null<br> <font color="#0076b3">GenericHttpMessageConverter<?> genericConverter =<br> (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); </font><br>
7. 通过目标参数类型和contentType等信息调用当前转换器的canRead方法判断当前转换器是否匹配<br>
如果message.hasBody()有内容<br>
8. 循环执行RequestBodyAdvice#supports返还true的RequestBodyAdvice#beforeBodyRead方法<br> (#1.2.5.1.4.2.2中在实例化RequestResponseBodyMethodProcessor的时候有将那些标注了<br> @ControllerAdvice且实现了RequestBodyAdvice或ResponseBodyAdvice接口的bean传进来)<br> <font color="#0076b3">HttpInputMessage msgToUse =<br> getAdvice().beforeBodyRead(message, parameter, targetType, converterType);</font><br>
springboot中默认添加了JsonViewRequestBodyAdvice<br>在beforeBodyRead中会处理那些标注了JsonView注解的参数<br>将inputMessage包装成MappingJacksonInputMessage对象<br>在../9.1中会判断inputMessage是否为MappingJacksonInputMessage类型来对JsonView进行处理<br>
JsonView使用示例
9. 执行反序列化操作<br> <font color="#0076b3">body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :<br> ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));</font><br>
1. 判断inputMessage是否为MappingJacksonInputMessage,是的话处理JsonView<br>
2. 通过objectMapper执行反序列化操作
10. 循环执行RequestBodyAdvice#afterBodyRead方法<br> <font color="#0076b3"> body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);</font>
如果message.hasBody()没有内容
11. 循环执行RequestBodyAdvice#handleEmptyBody方法<br> <font color="#0076b3">body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);</font><br>
跳出循环
如果此处./3中定义的body仍未 NO_VALUE<br>
12. 如果符合右侧中的某种情况则 retrun null<br>
httpMethod 为 null <br>
httpMethod不是post,put,patch中的一种<br>
contentType为null 且请求体中没有数据
13. 不存在./12中的情况则抛出HttpMediaTypeNotSupportedException
14. <font color="#0076b3">return body;</font><br>
4. 如果转换出来的arg为null且@RequestBody的required值为true则抛出HttpMessageNotReadableException<br>
5. <font color="#0076b3">return arg;</font>
3. 形参类型的首字母小写,并不是取形参名称<br> <font color="#0076b3"> String name = Conventions.getVariableNameForParameter(parameter);</font><br>
4. 和../1.7逻辑一样<br> <font color="#0076b3">WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);</font>
5. 4. 和../1.9一样会检验转换对象的合法性,只不过这里是抛出MethodArgumentNotValidException异常<br> <font color="#0076b3"> validateIfApplicable(binder, parameter);</font>
6. 和../1.12有些类似,不过只会将bindingResult放入mavContainer中供有校验错误且下一个参数为Errors类型时ErrorsMethodArgumentResolver处理器设值处理<br> <font color="#0076b3">mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());</font>
7. 处理参数是Optional的情况<br> <font color="#0076b3"> return adaptArgumentIfNecessary(arg, parameter);</font><br>
9. 最终返回实参数组args<br> <font color="#0076b3"> return args;</font>
2. 通过上一步得到的实参数组进行反射调用目标方法<br> <font color="#0076b3"> return doInvoke(args);</font>
2. 如果处理器方法有@ResponseStatus注解,则为response设置status<br> <font color="#0076b3"> setResponseStatus(webRequest);</font><br>
3. 如果./1 中返回值为null,且满足右侧任一条件,则设置mavContainer请求处理状态为已处理<br> 直接return方法,不进行下面的处理<br> <font color="#0076b3"> mavContainer.setRequestHandled(true);</font><br>
1. Request的NotModified为true<br>
2. 有@ResponseStatus注解标注<br>
3. mavContainer.isRequestHandled()已经为true了
4. 如果./1中返回值不为null,但@ResponseStatus存在reason值则也设置mavContainer请求处理状态为已处理,并返回方法<br>
5. 设置mavContainer请求处理状态为未处理<br> <font color="#0076b3"> mavContainer.setRequestHandled(false);</font>
6. 通过../6中设置的returnValueHandlers.handleReturnValue对返回值进行处理<br> <font color="#0076b3"> this.returnValueHandlers.handleReturnValue<br> (returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</font><br>
1. 通过HandlerMethodReturnValueHandler #supportsReturnType方法找出匹配的HandlerMethodReturnValueHandler <br> <font color="#0076b3">HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);</font>
2. 如果没有找到匹配的handler则抛出IllegalArgumentException异常
3. 调用匹配的HandlerMethodReturnValueHandler#handleReturnValue方法对返回值进行处理<br> <font color="#0076b3">handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);</font>
14. 获取模型和视图<br> <font color="#0076b3"> return getModelAndView(mavContainer, modelFactory, webRequest); </font><br>
1. 更新Model<br> <font color="#0076b3"> modelFactory.updateModel(webRequest, mavContainer);</font><br>
1. 取出mavContainer中的ModelMap<br> <font color="#0076b3">ModelMap defaultModel = container.getDefaultModel();</font>
2. container中的session状态为complete(即调用了SessionStatus.setComplete())<br> 如果是则则清除@SessionAttributes中指定缓存在session中的那些属性<br> <font color="#0076b3">this.sessionAttributesHandler.cleanupAttributes(request);</font><br>
3. 如果container中的session状态不为complete则将defaultModel中被@SessionAttributes注解指定需要<br> 缓存到session中的属性存储到session中<br> <font color="#0076b3"> this.sessionAttributesHandler.storeAttributes(request, defaultModel);</font>
2. 如果mavContainer中的请求处理状态为true,则return null,不进行下面的创建ModelAndView操作<br> 像标注有@ResponseBody注解的Controller或方法在../13.6.3中就会将mavContainer的请求处理状态为true<br> <font color="#0076b3"> mavContainer.isRequestHandled()</font><br>
3. 创建ModelAndView
5. 处理没有设置Cache-Control请求头的情况<br>
6. <font color="#0076b3"> return mav;</font>
12. 异步处理相关
13. 如果mv不为null且!mv.hasView(),则设置默认的视图名称<br> <font color="#0076b3">applyDefaultViewName(processedRequest, mv);</font><br>
14. 调用拦截器器的postHandle方法<br> <font color="#0076b3"> mappedHandler.applyPostHandle(processedRequest, response, mv);</font><br>
15. 处理转发结果<br> <font color="#0076b3">processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);</font><br>
1. 如果上面的步骤方法异常,会进入此处的异常处理逻辑<br>
2. mv != null 并且 !mv.wasCleared() 会进入视图渲染<br> (像标注有@ResponseBody的控制器或方法就直接跳过这一步了)<br> <font color="#0076b3"> render(mv, request, response);</font><br>
1. 通过localeResolver解析local,并设置到response中<br>
2. 从mv中取出viewName<br> <font color="#0076b3"> String viewName = mv.getViewName();</font>
3. 如果./2中取出的viewName不为空,则解析该viewName,主要是将字符串的名称解析成一个视图(会通过配置的prefix,suffix等值去查找jsp)<br> 如果解析出的view是null则会抛出ServletException异常<br> <font color="#0076b3"> view = resolveViewName(viewName, mv.getModelInternal(), locale, request);</font><br>
4. 如果./2viewName为null,则直接从mv中去取视图,如果直接去取也没取到则抛出ServletException异常<br> <font color="#0076b3"> view = mv.getView();</font>
6. 对视图进行渲染<br> <font color="#0076b3"> view.render(mv.getModelInternal(), request, response);</font>
3. 异步处理相关<br>
4. 执行拦截器的afterCompletion方法,注意能执行到此处说明没有异常的情况,所以异常参数传null<br> <font color="#0076b3">mappedHandler.triggerAfterCompletion(request, response, null);</font>
16. 如果./15中发生的是Exception 异常,执行拦截器的afterCompletion方法<br> <font color="#0076b3">triggerAfterCompletion(processedRequest, response, mappedHandler, ex);</font>
17. 如果./15中发生的是Throwable 异常,也需要执行拦截器的afterCompletion方法<br><font color="#0076b3"> triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("", err));</font>
18. 异步调用相关
18. 异步调用相关
19. 如果processedRequest != request,说明是文件上传,则清除文件上传使用的资源<br> <font color="#0076b3"> cleanupMultipart(processedRequest);</font><br>
5. ./1中有存储快照,则将快照中的属性内容重新存储到request属性中
6. 重置线程上下文
7. 发布请求处理事件ServletRequestHandledEvent<br> <font color="#0076b3"> publishRequestHandledEvent(request, response, startTime, failureCause);</font>
0 条评论
下一页