前言
上一節,已經分析了擷取執行鍊HandlerExecutionChain的過程,接下來繼續分析DispatcherServlet的doDispatch方法下面要執行的内容。
源碼分析
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
從擷取處理器的擴充卡接着分析,為什麼還需要這個擴充卡呢?到底需要适配什麼内容?這個問題分析完成後再回答,現在進入這個方法中,很顯然是從所有的擴充卡中找到通過支援此handler處理的。
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
在上面的代碼中注意的地方是this.handlerAdapters的内容是怎麼來的?ha.supports(handler)這個方法判斷了什麼?先來看看this.handlerAdapters的初始化(在DispatcherServlet類中)
/**
* Initialize the HandlerAdapters used by this class.
* <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
* we default to SimpleControllerHandlerAdapter.
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
//首先是一個變量(預設為true,但在web.xml中通過init-param可以設定成false)
//意思是檢測所有的handlerAdapters
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
//如果設定成了false,那麼嘗試擷取名稱為“handlerAdapter”的bean執行個體
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
//如果都沒有,那麼就加載預設配置的擴充卡(在DispatcherServlet.properties檔案中)
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
}
}
}
總之,上面的初始化過程是,先判斷是否需要檢測加載上下文中所有的擴充卡(預設),如果不是則嘗試加載名稱 是“handlerAdapter”的擴充卡。如果都沒有,則加載預設配置的擴充卡。
org.springframework.web.servlet.HandlerAdapter=\
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
可以看到它配置了三種擴充卡,分别為:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter,各自的作用是:
- HttpRequestHandlerAdapter,http請求處理器擴充卡,僅僅支援http請求的适配,它不需要傳回值,隻是簡單的将http請求的request和response對象傳遞給http請求處理器去實作。主要應用于http的遠端調用的實作上。這種類型的擴充卡,所有的handler處理器需要實作HttpRequestHandler接口,并實作void handlerRequest(HttpRequest,HttpResponse)方法。
- SimpleControllerHandlerAdapter,簡單控制處理器擴充卡,它是将HTTP請求适配到一個控制器來實作處理。所有的業務都是在這個控制器的實作類中處理的。所有的handler處理器都需要實作Controller接口或者是繼承AbstractController抽象類,并且需要實作ModelAndView handler(HttpRequest,HttpResponse,handler)方法,這個方法傳回了ModelAndView對象,用于後續的模闆渲染操作。
- AnnotationMethodHandlerAdapter,注解方法處理器擴充卡,它的實作是基于注解的,需要結合注解的方法映射和方法處理器協同工作。通過解析聲明在注解控制器的請求映射資訊來解析響應的處理器來處理目前的http請求,在處理的過程中,通過反射發現處理器方法的參數,調用處理器方法,同時映射傳回值到模型和控制器對象,最終傳回模型和控制器對象給主要制器DispatcherServlet。
是以,在這裡我們就可以了解為什麼需要擴充卡,主要是spring中使用的handler之間可以沒有特殊的關系,但為了達到統一處理的目的,spring提供了不同handler的擴充卡去适配不同類型的handler處理器,其實除了上述的三種擴充卡還有SimpleServletHandlerAdapter、RequestMappingHandlerAdapter與上面的SimpleControllerHandlerAdapter原理大同小異。這樣使得handler的實作更加多樣化,與隻能實作handler接口的方法相比也更加靈活。
分析完了Adapter擴充卡,接下來看看ha.supports(handler)是如何判斷的,它是在handlerAdapter子類中實作的,不同的子類實作不太一樣,又大緻相同
HttpRequestHandlerAdapter中的實作
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
AnnotationMethodHandlerAdapter
@Override
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
}
RequestMappingHandlerAdapter這個與之前的有所差別,上面的都是直接實作了HandlerAdapter接口,而此類是繼承了AbstractHandlerMethodAdapter。它主要是判斷目前handler對象是否是HandlerMethod類的執行個體和是否支援目前handlerMethod
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
* @param handler the handler instance to check
* @return whether or not this adapter can adapt the given handler
*/
@Override
public final boolean supports(Object handler) {
//supportsInternal總是傳回true
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
到此我們已經擷取到了處理器的擴充卡,接着回到DispatcherServlet的doDispatch方法,看下面一段代碼。先判斷是GET請求,再判斷重新getLastModified的時間戳是否新于上一次請求的時間,如果是最新的則說明請求的内容已經修改,繼續往下執行,如果沒有修改,則直接傳回一個304的狀态碼,不再往下執行,浏覽器拿304碼後,直接拿緩存的資料展示。
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
接下來,就是執行執行鍊中的攔截器的preHandler方法,如果任何一下攔截器的此方法傳回了false,就會調用triggerAfterCompletion進行資源清理,此方法也是周遊所有攔截器調用攔截器的afterCompletion()方法,然後傳回false,doDispatch方法不會再往下執行。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
再往下,這才是重點,就是擴充卡執行handler方法,傳回ModelAndView,上面也說了不同的handler處理器對應不同的擴充卡,是以此方法也肯定是由各子類去實作的
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在這裡我們主要是看AbstractHandlerMethodAdapter
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
再進入handleInternal,就來到了RequestMappingHandlerAdapter類的handleInternal方法中,我們來看看這個方法做了什麼
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
/**
檢查請求,調用它父類WebContentGenerator中的checkRequest()方法,判斷支援的請求方式
是否包含目前請求的方式,如果supportedMethods不為空且不支援目前請求方式,
會抛出著名的HttpRequestMetohdNotSupportedException。
如果需要session且從目前請求獲得不到session,
同樣抛出HttpSessionRequiredException異常
*/
checkRequest(request);
/**
判斷headlerMethod上是否有@SessionAttributes的注解,如果有
則在response上設定Cache-Control
*/
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
//如果沒有,則預處理response,判斷cacheControl是否為空
//如果不空則設定,如果為空則清空response的cacheControl
prepareResponse(response);
}
// Execute invokeHandlerMethod in synchronized block if required.
//判斷是否需要同步機制來執行invokeHandlerMethod
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handlerMethod);
}
}
}
//如果不需要則直接執行method
return invokeHandlerMethod(request, response, handlerMethod);
}
進入到RequestMappingHandlerAdapter類的invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//封裝一個RequestAttribute,友善取各參數
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//擷取一個綁定工廠類,主要用于建立WebDataBinder對象
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//擷取一個模型工廠類
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
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;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
上面第二步擷取WebDataBinderFactory,它的作用是建立WebDataBinder對象。我們都知道用戶端無論什麼類型的參數,最終都是以位元組的形式傳給服務端,然後由request.getParameter方法以字元串的形式取出來。這樣就需要有一個轉換類把這個字元串類型轉給我們需要的類型,比如:2019-02-28轉換成Date類型等等。WebDataBinder對象就是我們需要的轉換工具,它不需要我們去建立,我們隻需要使用類似 binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true))的操作向它注冊相應的PropertyEditor(最常見的做法實作WebBindingInitializer接口)。PropertyEditor調用它的void setAsText(String text)方法實作資料轉換的過程可以将字元串轉換成真正的資料類型,這個是sun包中的類,比如:BooleanPropertyEditor,LongPropertyEditor等等。除了使用PropertyEditor轉換外,我們還可以使用org.springframework.core.convert.converter.Converter<S, T>,并且把它注冊到org.springframework.context.support.ConversionServiceFactoryBean中來實作類型的轉換。
扯遠了,我們再回到ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);方法
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first
//首先周遊所有@ControllerAdvice注解的類,從這些類中找到@InitBinder的方法
//封裝成InitBinderMethod對象加入到list中
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
Object bean = entry.getKey().resolveBean();
for (Method method : entry.getValue()) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
}
//說明可以有多個@InitBinder
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
先是從initBinderCache中擷取,如果沒有取到,就重新使用查找器找,INIT_BINDER_METHODS這是一個Filter,可以看到在目前類中找@InitBinder注解的方法,再放到緩存中
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
}
};
從這裡可以看出所建立的WebDataBinderFactory,其實是ServletRequestDataBinderFactory
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
到這裡我們已經知道,建立了一個WebDataBinderFactory,它包含了很多類型轉換器,接下來就是建立ModelFactory
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
// Global methods first
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);
}
過程和建立WebDataBinderFactory類型,ModelFactory的作用是在控制器方法調用前初始化Model模型,調用後對Model模型進行更新操作。既然binderFactory和modelFactory都已經被建立出來了,接下來就是對handlerMethod進行再次封裝了,接着invokeHandlerMethod方法往下走
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
//ServletInvocableHandlerMethod的作用就是對處理器方法的傳回
//值進行相關的處理,同時還有ResponseStatus
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//執行個體化容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//把request中的DispatcherServlet.INPUT_FLASH_MAP,重定向
//參數注入到容器的model模型中,FlashMap的作用是在redirect中傳遞參數。
//重定向是會生成新的request,傳遞參數就不能直接用request進行傳遞
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
...
return getModelAndView(mavContainer, modelFactory, webRequest);
}
執行個體化ModelAndViewContainer容器,它主要是用來傳回Model對象的,在容器中有兩種Model一個是defaultModel,另一個是redirectModel,redirectModel用于傳遞redirect時的參數。
接下來看看modelFactory.initModel(webRequest, mavContainer, invocableMethod);做了什麼事情
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, mavContainer);
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
}
}
}
從request中擷取@SessionAttribute的屬性,先存在map結構中,然後執行了invokeModelAttributeMethods(request, mavContainer);方法
/**
* Invoke model attribute methods to populate the model.
* Attributes are added only if not already present in the model.
*/
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
//把@ModelAttribute注解的handlerMethod中的模型添加到容器中的model中
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
if (mavContainer.containsAttribute(modelName)) {
continue;//隻有當容器中不存在@ModelAtrribute中的屬性時才加入
}
//取出所有的參數并且利用容器中包含的參數解析器進行解析,完成後
//取得HandlerMethod屬性方法和對應的bean對象,利用反射機制執行此方法
//擷取傳回值
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
//如果HandlerMethod方法不是void的
if (!attrMethod.isVoid()){
//如果@ModelAtrribute注解設定了value值,則此值
//作為傳回值的名稱,如果沒有設定,則選擇被@ModelAtrribute注解
//注解的handlerMethod的傳回類型名稱(首字母小寫)作為model中的key
//如果這個傳回類型是數組或集合,那麼這個名稱是傳回類型名稱再拼接上"List"
//如果傳回類型是String或者是map,那麼key就是map或string
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
}
}
}
}
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//解析擷取參數
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
//執行
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//取到所有的參數
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//使用對應的解析器去解析參數
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
return args;
}
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//反射機制執行方法
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
}
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Invoking [");
sb.append(getBeanType().getSimpleName()).append(".");
sb.append(getMethod().getName()).append("] method with arguments ");
sb.append(Arrays.asList(args));
logger.trace(sb.toString());
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;
}
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
}
else {
Method method = returnType.getMethod();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
//這個方法是判斷如果傳回的是object類型,那麼就會通過實作的傳回類型推測出
//類的簡稱
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
再回到initModel()方法上,分析接下來的代碼
//周遊handlerMethod的參數,判斷是否有被@ModelAttribute注解的
//如果有,則繼續判斷這個參數和類型是否和目前handlerMethod所在類
//中的@SessionAttribute注解中的參數和類型一緻
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
//把名稱和值添加到model中,這裡的model是defaultModel
mavContainer.addAttribute(name, value);
}
}
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
List<String> result = new ArrayList<String>();
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(parameter);
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
result.add(name);
}
}
}
return result;
}
接着再回到
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
//處理一些異步請求
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);
}
//調用invokeAndHandle方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
我們來看看invokeAndHandle,從給定請求中解析handlerMethod的參數,調用此方法,并擷取handlerMethod的傳回值,具體的過程與上面的執行屬性方法類似
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//如果有使用@ResponseStatus注解來設定響應狀态時則設定responseStatus
setResponseStatus(webRequest);
//如果傳回值為空,則狀态是否是request請求或者傳回狀态有值時
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);//直接使用response來處理傳回
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
//如果不使用response來處理,則設定為false,使用view來處理傳回
mavContainer.setRequestHandled(false);
try {
//周遊所有的傳回值處理器,通過supportsReturnType()
//來找到支援此傳回值類型的處理器
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
我們再回到RequestMappingHandlerAdapter中的invokeHandlerMethod()方法,接下來,還剩下
return getModelAndView(mavContainer, modelFactory, webRequest);
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//檢視下面updateModel中的解釋
modelFactory.updateModel(webRequest, mavContainer);
//如果容器采用的是response直接處理的方式,說明不采用view
//的方案,直接傳回null即可
if (mavContainer.isRequestHandled()) {
return null;
}
//采用的是view方案,擷取預設的model,将此model和視圖名稱
//直接填充到ModelAndView中,建立一個ModelAndView出來
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
//return (this.view instanceof String);如果容器中的view不是string的話
//就設定此視圖對象,如果是string說明是邏輯視圖名稱,等待下一步處理
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
//如果model是RedirectAttributes的執行個體,
//那麼說明是model是重定向所需要的屬性,我們把model填充到FlashMap即可
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
//擷取預設的model對象,判斷目前會話是否完成,如果完成
//則清理相關的資源,如果沒有完成,則把目前request中
//的model對象儲存在sessionAttributeStore友善下次請求
//
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}
再傳回到上一步RequestMappingHandlerAdapter中的hanlderInternal()方法中,方法已經執行完成,接着傳回到RequestMappingHandlerAdapter中方法也執行完成,接着再傳回到AbstractHandlerMethodAdapter中的handler方法,即在doDispatch方法中執行的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());此時我們得到了ModelAndView對象。接下來就是如何把Model綁定到相應的view的問題了。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
...........
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果mv中沒有視圖名稱,則設定預設的視圖名稱
applyDefaultViewName(processedRequest, mv);
//執行 在執行鍊中的所有攔截器的postHandle方法對傳回的結果再次處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//對傳回結果進行處理,詳見下面 processDispatchResult方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//判斷是否有異常存在,如果有則再判斷是否是ModelAndViewDefiningException
//如果是則把視圖解析成ModelAndViewDefiningException需要的類型
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//如果是其它異常,
//就讓HandlerExceptionResovlers中的異常解析器來處理
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//如果mv不為空且mv中的model和view沒有被清理
//則執行render
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
//render的作用就是判斷mv中的視圖是否是邏輯視圖名稱,如果是,
//則去視圖解析器中找到對應的實體視圖對象,找到後直接執行視圖的render方法
//完成渲染工作
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
//調用它本身的render方法把model對象填充到view中,完成渲染工作
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
總結
到現在為止,dispatch方法主體才執行完成,後續還有一些清理的方法不再描述。可以看到springmvc在處理請求的時候還是先複雜的,這個主要是由于springmvc具有很強的擴充性,在請求的名階段都可以幹預請求的處理和處理結果。