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