他是干什么
RequestMappingHandlerAdapter处理执行我们最常用的@requestMapping 的方法,也是目前mvc提供的最常用最复杂的 handlerAdapter相对于其他的。他功能很简单:通过反射执行我们标记 @requestMapping 的方法。但是他复杂在,需要我们对执行@requestMapping 入参的绑定(因为没有对入参做要求,所以很实用,但是处理起来会面临各种各样的情况,所以很复杂),和一些我们定义好的前置处理步骤比如@initbinder,@modeAttribute等标记代码块的执行,要给对参数,有的都要给。
初始化
我们先从初始化入手看他的结构,一看到InitializingBean,找afterPropertiesSet如下
@Override
public void afterPropertiesSet() {
initControllerAdviceCache();//
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
先看第一行
//找到@ControllerAdvice 和ModelAttribute,InitBinder的method和bean
initControllerAdviceCache();
/*
* 对应源码
*/
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
}
// 找到所有@ControllerAdvice标记的bean
List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(beans);//排序
List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
for (ControllerAdviceBean bean : beans) {
//找到ModelAttribute标记,同时没有被RequestMapping标记的method
Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(bean, attrMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @ModelAttribute methods in " + bean);
}
}
//找到@initbinder标记的method
Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(bean, binderMethods);
if (logger.isInfoEnabled()) {
logger.info("Detected @InitBinder methods in " + bean);
}
}
//看是不是实现了RequestBodyAdvice接口,实现了放到一个list中
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (logger.isInfoEnabled()) {
logger.info("Detected RequestBodyAdvice bean in " + bean);
}
}
//看是不是实现了ResponseBodyAdvice接口,实现了放到一个list中
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
requestResponseBodyAdviceBeans.add(bean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice bean in " + bean);
}
}
}
//把所有的bodyAdvice 放入一个requestResponseBodyAdvice,以备后面使用
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
经过以上操作就把所有的spring中的@ControllerAdvice 注解的bean,和对应的@initbinder,@modeAttribute的method还有两个request/ResponseBodyAdvice都放到我们的RequestMappingHandlerAdapter中的,以备后面使用也就是完成的initControllerAdviceCache 初始化initControllerAdviceCache缓存。
后面三种就是加载各种解析器
调用过程
程序入口是handleInternal方法,看一下源码
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//校验session是否必须存在,还有是否支这个请求
checkRequest(request);
// 是否对session同步
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果没有可用的HttpSession ->不需要互斥
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果根本不要同步
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//看看response报文头中是否包含cache_control
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
//判断当前这handler是否有@sessionAttribute,如果有就解析出来,放到缓存中准备下次使用
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
//设置返回缓存过期时间
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
我们从头开始看
checkRequest(request);
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// 得到当前请求的方法类型,看看当前handleradapter是否支持这个方法
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(
method, StringUtils.toStringArray(this.supportedMethods));
}
// 看看是否必须要这个session,如果必须没有抛异常
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
第二段执行方法后面细说,剩下的一段就是
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
这段代码总的意思是:
一般情况下,如果返回报文中没有对缓存的特殊控制,如果有@sessionAttribute则组织使用缓存,否则就什么也不做
下面我们看重点,执行方法
mav = invokeHandlerMethod(request, response, handlerMethod);
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//看下面 1
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//看下面 2
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//看下面3
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//新建传递参数的 mavcontainer容器,并把相应的参数设置进去
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//设置flashMap的参数进去
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//用modelFactoey把 sessionAttribute和modelAttribute的参数设置进去
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//这个是在mavcontainer中会用到后面细说
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//做一些异步处理相关操作,后面细说
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
//执行请求
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//看 4
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
我们看一下这个方法,首先封装了一个webRequest,这个用于argumentResolver解析参数时使用,然后就是对 WebDataBinderFactory, ModelFactory, ServletInvocableHandlerMethod进行定义和初始化
- WebDataBinderFactory
用来创建WebDataBinder,WebDataBinder主要用来参数绑定,实现参数跟String之间的类型转换,看一下源码
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//取得bean的class
Class<?> handlerType = handlerMethod.getBeanType();
//看看是不是这个controller已经在缓存中有了
Set<Method> methods = this.initBinderCache.get(handlerType);
// 如果没有
if (methods == null) {
//查找这个controller看看是不是有initbinder,有的话加载到缓存中,下次使用
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
//自定义保存initbinder方法的零时变量
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
// 先找到符合条件的全局 initbinder,添加到initbindermethods中
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
// 先添加全局的initbinder
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
}
//再添加当前这个controller的
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
//创建factory返回
return createDataBinderFactory(initBinderMethods);
}
从上面的源码可以看出,主要是找到所有的initbinder 来创建WebDataBinderFactory,注意下面这个MethodIntrospector.selectMethods 找不到也会返回一个空set,不是null,这个可以确保我们只用查一次,不用发现发没有,次次查
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
- ModelFactoey
用来处理model,主要是(1)在处理器处理之前对model初始化 (2)在处理完后对model进行参数更新
先说初始化,初始化主要是做三部分内容
- 将原来的@sessionAttribute 中的值设置到model中。
- 执行注释了@modelAttribute 的方法,并将值设置到model中。
- 处理器注释了@modelAttribute 的参数,如果同时在@sessionAttribute中也配置了,而且在mavContainer中还没有值,则从全部的sessionAttribute中找到值并设置上。
更新是先对SessionAttributes进行设置,设置的规则是如果处理器设置了sessionStatus中的setComplete 就将sessionAttributes清空,否则就将model中的参数设置给sessionAttribute,然后按照需要把给model设置参数对应的bindingResult下面看一下源码
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
// 获取SessionAttributesHandler,也就是@sessionAttribute标签的一些信息
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
//同上面initbinder的逻辑,找到modeAttribute 的方法
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
// 和上面的一样,先是公共的,再是私有的
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
}
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
看一下 new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); 可以看出创建他需要 一个是要用的 initbinder一个是modelAttributes,还有一个是sessionAttributesm给齐了
- ServletInvocableHandlerMethod核心方法请求的处理就是通过他来执行,参数绑定,和请求处理都是他来完成,首先把handlerMethod 封装为 servletInvocableHandlermethod,然后再把解析器设置进去
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
4.getModelAndView(mavContainer, modelFactory, webRequest); 主要做三件事1)调用modelFactory更新了model。2)根据mavcontainer 创建了modelandview。3)如果mavcontainer是redirectAttribute类型的就将值设置到 falshmap中
getModelAndView(mavContainer, modelFactory, webRequest);
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//更新model
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
//创建mav
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
//设置falshMap
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
小结
总的来说逻辑是很简单的,就是用来很多陌生的组件,后面有专门的分析,主要流程的就是绑定参数,执行请求,处理返回值。
1)参数绑定
参数的主要来源就是有6类参数的主要来源就是有6类
- request中参数
- cookie中的参数
- session中参数
- falshMap中的参数
- @sessionAttribute中的参数
- @modelAttributes中的参数
前面三个在request中处理,falshMap是直接在RequestMappingHandlerAdapter处理,后面两个在ModelFactory中处理
2) 处理请求,用的是ServletInvocableHandlerMethod
3) 处理收尾,主要是更新model中的缓存,和modelAndView的创建