来到获取handler对应的适配器,经过上一篇的讲解,我们知道这个handler就是HandlerMethod类型了
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
我们点击supports可以看到:
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
第一个AbstractHandlerMethodAdapter是支持HandlerMethod 类型的,就这样,我们就得到了HandlerAdapter 适配器了;
然后下面就是我们关注mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
return handleInternal(request, response, (HandlerMethod) handler);
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//先声明ModelAndView结果
ModelAndView mav;
//检查请求是否被支持,包含两个逻辑:
//1.判断本实例中的supportedMethods是否包含请求方法,默认支持所有请求方法,就是get,post这些method;
//2.如果配置的需要session属性为true,检查请求是否包含session,默认不需要session
checkRequest(request);
//处理时是否对session加锁,默认为false
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
//获取session对象
HttpSession session = request.getSession(false);
//如果session不为空
if (session != null) {
//获取session中的锁对象
Object mutex = WebUtils.getSessionMutex(session);
//加锁后执行调用处理器方法逻辑
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
//没有session,忽略加锁,直接执行调用处理器方法逻辑
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all..
//配置的无需锁session,则直接调用处理器方法逻辑
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//如果响应结果不包含缓存控制头
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
//如果该处理器方法包含SessionAttribute
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
//应用SessionAttribute的缓存策略,默认cacheSecondsForSessionAttributeHandlers为0,表示不缓存
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
//不包含SessionAttribute,准备请求。内部逻辑应用配置的缓存策略。本适配器默认没有缓存策略,故所有请求都不返回缓存响应头
prepareResponse(response);
}
}
//返回结果invokeHandlerMethod的执行结果;
return mav;
}
从上面可以看到,重点就是invokeHandlerMethod方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//把请求与响应封装为一个ServletWebRequest对象,用于后续处理逻辑使用
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//1.准备组件工厂
//获取处理器方法对应WebDataBinderFactory工厂,该工厂用于获取处理器方法对应的WebDataBinder组件
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//调用getModelFactory方法获取当前处理器方法对应的Model工厂,该工厂用于获取处理器方法对应的Model
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//2.准备可调用处理器方法实例
//创建一个Servlet下可调用处理器方法,该方法内部直接使用new ServletInvocableHandle(handleMethod)创建实例
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//本组件的参数解析器不为空,则为创建的可执行方法设置参数解析器,初始化时参数解析器会被初始化,所以这里不为null ->这里的初始化是指这个RequestMappingHandlerAdapter类,它实现了InitializingBean接口,会去执行afterPropertiesSet方法
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//本组件的返回值处理器不为空,则为创建的可执行方法设置返回值处理器,初始化时返回值处理器会被初始化,所以这里不为null
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//设置可执行方法的DataBinder工厂,用于获取WebDataBinder实例
invocableMethod.setDataBinderFactory(binderFactory);
//设置可执行方法的参数获得器,用于获取方法上的参数名
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//3.准备ModelAndView容器并初始化
//创建用于处理过程中使用的ModelAndView容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//向当前ModelAndView容器的Model中添加输入FlashMap中的所有属性
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//初始化Model,包含调用Model相关的初始化方法,如@ModelAttribute注解标记的方法
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//在重定向时忽略默认的Model属性值,只考虑重定向Model的属性值,默认为true
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//4.准备异步请求处理组件
//根据当前请求与响应创建异步请求
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();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//5.调用处理器方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//如果处理完成后,开启了异步请求并在处理中,说明返回值为一个异步的结果,直接返回null,等待异步结果返回,再执行上面的获取异步结果逻辑
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//6.获取ModelAndView结果
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
//标记请求完成
webRequest.requestCompleted();
}
}
1.准备组件工厂:
关于@InitBinder的一些用法:可以看下大佬写的blog.csdn.net/wang0907/ar…
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//获取处理器方法所在的Bean类型
Class<?> handlerType = handlerMethod.getBeanType();
//尝试从Initbinder缓存中获取当前处理器类型对应的所有标记了@InitBinder注解的方法
Set<Method> methods = this.initBinderCache.get(handlerType);
//如果为空,说明缓存中还没有,进入获取逻辑
if (methods == null) {
//找到处理器类型中所有标记了注解@InitBinder的方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
//添加到缓存,以提高后续处理速度
this.initBinderCache.put(handlerType, methods);
}
//用于保存全部标记了@InitBinder方法的结果列表,标记了@InitBinder的方法会被封装为InvocableHandlerMethod可调用处理器方法,一遍调用该方法
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
//全局@InitBinder方法放在结果列表前面,@ControllerAdvice标记的处理器增强Bean中的@InitBinder即为全局的
//遍历全局@InitBinder增强器缓存(缓存数据是来源于组件初始化时),controllerAdviceBean和methodSet是这个Bean中标记@InitBinder注解的方法的集合
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
//先判断全局InitBinder是否被应用到当前处理器类型,判断依据是@ControllerAdvice注解中配置的一些属性
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
//如果可以被应用,则把增强器解析为Bean实例,为什么需要这个Bean?因为反射调用方法时需要
Object bean = controllerAdviceBean.resolveBean();
//编辑集合
for (Method method : methodSet) {
//把bean和method封装为InvocableHandlerMethod
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
//全局的添加完后,添加当前参数handlerMethod所在的Bean的@InitBinder方法
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
//创建WebDataBingder工厂
return createDataBinderFactory(initBinderMethods);
}
类似上面@InitBinder的流程,这里时@ModelAttribute和@SessionAttributes的获取,@ControllerAdivce的也是为全局的
//获取Model工厂,传入参数为处理器方法与WebDataBinder工厂
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//获取SessionAttributes的处理器,用于处理处理器Bean类型上标记的@SessionAttributes注解,方法里边有个handlerMethod.getBeanType
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取处理器Bean类型
Class<?> handlerType = handlerMethod.getBeanType();
//从ModelAttribute缓存中获取Bean类型对应的@ModelAttribute标记的方法
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
//若缓存中没有,则进入获取逻辑,获取处理器类型中标记了@ModelAttribute注解且未标记@RequestMapping注解的方法(在MODEL_ATTRIBUTE_METHODS中),作为ModelAttribute方法
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
//放入缓存中,加快下次处理逻辑
this.modelAttributeCache.put(handlerType, methods);
}
//创建一个列表,用于保存@ModelAttribute注解标记方法的InvocableHandleMethod,可调用封装
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
// Global methods first
//全局的@ControllerAdvice标记的处理器增强中的@ModelAttribute方法优先
//遍历处理器增强Bean(被@ControllerAdvice标记的Bean)中的@ModelAttribute注解方法缓存,该缓存内容在组件初始化时填充,controllerAdviceBean是对应的处理器增强Bean的封装:ControllerAdviceBean,methodSet是这个处理器增强Bean中的全部@ModelAttribute标记的方法
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
//通过@ControllerAdvice的属性进行过滤,判断是否可悲应用到当前处理器Bean中去
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
//解析处理器增强Bean为实例
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
//遍历对应的方法的集合,创建ModelAttribute可调用方法,添加到结果列表中
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//全局@ModelAttribute方法添加完成后,添加当前处理器Bean中的@ModelAttribute方法
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
//返回Model工厂实例,封装全部@ModelAttribute的可调用方法,WebDataBinder工厂与SessionAttrubutes处理器
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
这个是处理@SessionAttributes注解的一个方法,主要当前Bean头上的注解中的names属性和types属性添加到SessionAttributes处理器中,以供后续@SessionAttributes注解的相关处理逻辑使用。
我们看下它的构造函数:
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
Collections.addAll(this.attributeNames, ann.names());
Collections.addAll(this.attributeTypes, ann.types());
}
this.knownAttributeNames.addAll(this.attributeNames);
}
来到主流程的第3点:准备ModelAndView容器并初始化
//创建ModelAndView容器,构造时其内部已经创建了用于保存Model属性的ModelMap实例
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//把重定向输入的FlashMap中所有属性添加到当前ModelAndView容器的Model属性中,以供本次处理逻辑使用(FlashMap的作用就是把上一次的输出FlashMap作为这一次的输入FlashMap)
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//这里使用Model工厂初始化新创建的ModelAndView容器
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//设置容器的属性,如果不忽略,默认Model中的属性将通过url的路径参数传递
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
让我们看一下modelFactory.initModel(webRequest, mavContainer, invocableMethod);
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
//使用SessionAttributes处理器,先从Session中获取@SessionAttribute注解声明的Session属性(Object value = session.getAttribute(name);)
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
//把@SessionAttributes声明的Session属性全部合并到ModelAndView容器中,以供后续处理逻辑使用。这就是@SessionAttributes的工作原理了
container.mergeAttributes(sessionAttributes);
//调用全部@ModelAttribute注解标记的方法,把方法返回值放入ModelAndView容器的Model中
invokeModelAttributeMethods(request, container);
//在findSessionAttributeArguments方法中,获取了HandlerMethod中所有标记了@ModelAttribute的参数
//再判断该类型是否在@SessionAttributes的types属性中,如果是,则把获取该参数上@ModelAttribute注解的name属性添加到方法的返回值列表中
for (String name : findSessionAttributeArguments(handlerMethod)) {
//如果ModelAndView容器不包含name属性,则从Session中获取该属性,添加到容器中
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
这里调用了@ModelAttribute注解标注的方法及初始化ModelAndView容器中Model属性外,还包括把@SessionAttributes注解生命的所有Session属性放入容器中。
被标记为@ModelAttribute的参数,如果其对应类型在@SessionAttributes注解的types中时,也会被作为Session属性名与对应的属性值放入容器中;
来看下这个方法 invokeModelAttributeMethods(request, container);
// 调用全部模型属性方法,向ModelAndView容器中添加相关属性
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
//遍历全部模型属性方法,直到为空\
while (!this.modelMethods.isEmpty()) {
//获取当前遍历的可调用模型属性方法(就是被@ModelAttribute注解的方法)
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
//检查该方法是否存在@ModelAttribute注解
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
//不存在,则状态异常,因为该方法的检查逻辑时通过判断是否存在这个注解进行添加,正常情况下不会触发
Assert.state(ann != null, "No ModelAttribute annotation");
//如果当前容器已经包括了模型属性方法生命的模型名,则不覆盖现有的属性
if (container.containsAttribute(ann.name())) {
//如果注解标记了binding = false,则把这个属性名添加到容器的不绑定列表中,用于在参数绑定时忽略此模型属性的绑定
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
//跳过,不覆盖现有属性
continue;
}
//ModelAndView容器中不包含该属性则执行下面逻辑
//根据请求执行模型属性方法,获取模型属性方法的返回值。
Object returnValue = modelMethod.invokeForRequest(request, container);
//如果方法声明返回值是void
if (modelMethod.isVoid()) {
//判断注解属性的value是否有值
if (StringUtils.hasText(ann.value())) {
if (logger.isDebugEnabled()) {
logger.debug("Name in @ModelAttribute is ignored because method returns void: " +
modelMethod.getShortLogMessage());
}
}
//跳过覆盖现有属性
continue;
}
//如果返回值不是void的话,获取方法的返回值
//根据返回值与类型获取返回的属性名,一般是从方法注解@ModelAttribute的name属性中获取该属性名
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
//判断是否声明了binding = false
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
@ModelAttribute的参数会优先去获取@ModelAttribute方法中的匹配值,优先于@SessionAttribues;比如get请求,这时候尽管我往session中设置了值,第二次请求get请求去获取@ModelAttribute标记的参数时,依然会是先拉到@ModelAttribute方法的值。在上面代码我们也知道如果ModelAndView中存在了这个属性的话,是不会进行覆盖的,且@SessionAttributes在@ModelAttribute方法前,为什么会这样呢?
因为:
是因为,下面有个操作会把Model中的值替换掉session中已有同名的属性,在第一次请求的时候,设置了Session,但是这时候@ModelAttribute方法已经在Model中了,然后执行完下面这个代码就把Session中的属性给替换了;
这个代码就是主流程的第6点:
return getModelAndView(mavContainer, modelFactory, webRequest);
modelFactory.updateModel(webRequest, mavContainer);这个方法
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
this.sessionAttributeStore.storeAttribute(request, name, value);
session.setAttribute(name, value);
以上就是@ModelAttribute和@SessionAttribues的原理了,在后面参数解析的时候,会用到这里面Model的值,还有@InitBinder的参数格式转换;
下面我们来讲一下第4个流程,异步管理那里;