11.SpringMVC 請求處理
基本概念
SpringMVC 通過 HandlerAdapter 的 handler 方法來呼叫請求處理函式。
在 DispatcherServlet 中根據請求路徑利用 Handlermapping 找到對應的 handler 後,首先檢查當前 Ioc 容器中所有可用的 HandlerAdapter ,再利用 HandlerAdapter 中的 supports 方法找到可以使用的HandlerAdapter。
首先來看它的繼承關係:
內部構造
該介面內部定義了定義了三個方法。
public interface HandlerAdapter {
// 是否支援該 HandlerMethod
boolean supports(Object handler);
// 根據 HandlerMethod 取得 ModelAndView
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
AbstractHandlerMethodAdapter
該類是 HandlerAdapter 介面的簡單抽象類,實現了介面定義的方法。
但也並未做真正的實現,而是留給了子類。下面來看它的原始碼:
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
public final long getLastModified(HttpServletRequest request, Object handler) {
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter
該類繼承了 AbstractHandlerMethodAdapter 類,真正意義上實現了 HandlerAdapter 介面定義的功能。
1.supportsInternal
預設返回 true,說明只要處理器是 HandlerMethod 類即可
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
2.getLastModifiedInternal
預設返回 -1.
protected long getLastModifiedInternal(HttpServletRequest request,
HandlerMethod handlerMethod) {
return -1;
}
3.handleInternal
該方法負責呼叫 HandlerMethod(處理器) ,並返回 ModelAndView 。
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
// 1.校驗請求
// 檢查是否支援當前 rqeuest 的 method 和 session
checkRequest(request);
// 2.判斷控制器是否存在 @SessionAttributes 註解
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// 2.1設定快取
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
// 2.2準備響應
prepareResponse(response);
}
// 預設為 false,為 true 表示在同步塊中執行 invokeHandlerMethod
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handlerMethod);
}
}
}
// 關鍵 -> 3.處理器呼叫
return invokeHandlerMethod(request, response, handlerMethod);
}
分析程式碼,該方法的具體過程如下:
- 1.校驗請求,即檢查是否支援當前 rqeuest 的 method 和 session
- 2.判斷控制器是否存在 @SessionAttributes 註解,有則設定快取,否則準備響應
- 3.處理器呼叫,返回 ModelAndView 。
ModelMap
該類繼承自 LinkedHashMap,說明它代表一組有序的對映集,用於儲存資料。
它的簽名如下:
public class ModelMap extends LinkedHashMap<String, Object>
再來看它的建構函式:
public ModelMap(String attributeName, Object attributeValue) {
addAttribute(attributeName, attributeValue);
}
public ModelMap addAttribute(String attributeName, Object attributeValue) {
// 儲存 <K,V>
put(attributeName, attributeValue);
return this;
}
ModelAndView
該類內部儲存了 ModelMap 和 View。
private Object view;
private ModelMap model;
public ModelAndView(String viewName, String modelName, Object modelObject) {
this.view = viewName;
addObject(modelName, modelObject);
}
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();
}
return this.model;
}
ModelAndViewContainer
該類表示一個容器,不僅包含了 ModelMap 、View 還有其他相關內容。
觀察它的成員變數可知:
private Object view;
private final ModelMap defaultModel = new BindingAwareModelMap();
private ModelMap redirectModel;
private final SessionStatus sessionStatus = new SimpleSessionStatus();
private boolean redirectModelScenario = false;
private boolean ignoreDefaultModelOnRedirect = false;
private boolean requestHandled = false;
ServletInvocableHandlerMethod
1.基本概念
該類實現了 HandlerMethod 類,具體繼承關係如下:
下面來看它的建構函式:
public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
initResponseStatus();
}
觀察程式碼,該類的建構函式執行過程如下:
呼叫父類,即 HandlerMethod 的建構函式 ,將 HandlerMethod 的成員變數賦值給自己的成員變數。
protected HandlerMethod(HandlerMethod handlerMethod) { this.bean = handlerMethod.bean; this.beanFactory = handlerMethod.beanFactory; this.beanType = handlerMethod.beanType; this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; }
提取 @ResponseStatus 註解的相關內容賦值給自己的成員變數
private void initResponseStatus() { ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class); if (annotation != null) { this.responseStatus = annotation.code(); this.responseReason = annotation.reason(); } }
2. invokeAndHandle
關鍵來看該類的 invokeAndHandle 方法, 它負責呼叫 HandlerMethod 中控制器的指定方法。
public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 關鍵 -> 1.執行控制器指定方法
Object returnValue =
invokeForRequest(webRequest, mavContainer, providedArgs);
// 2.設定響應狀態
setResponseStatus(webRequest);
// 3.判斷請求是否處理完畢,根據返回值來判斷。
if (returnValue == null) {
if (isRequestNotModified(webRequest) ||
hasResponseStatus() ||
mavContainer.isRequestHandled()) {
// 為 true ,表示方法呼叫完畢
mavContainer.setRequestHandled(true);
return;
}
}else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
// 存在返回值時,可能還需進行檢視解析
mavContainer.setRequestHandled(false);
try {
// 處理返回值,在 MavContainer 中設定 ViewName
// 並判斷其是不是【重定向請求】
this.returnValueHandlers.handleReturnValue(
returnValue,
getReturnValueType(returnValue),
mavContainer,
webRequest);
}catch (Exception ex) {
// 丟擲異常...
}
}
- 執行控制器指定方法
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 省略部分程式碼...
// 取得方法入參值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
// 利用反射執行該方法,並取得返回值
Object returnValue = doInvoke(args);
return returnValue;
}
protected Object doInvoke(Object... args) throws Exception {
// 設定方法的訪問許可權,BridgedMethod 表示控制器類的指定方法
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 關鍵 -> 利用反射執行該方法,Bean 表示控制器類名稱
return getBridgedMethod().invoke(getBean(), args);
}catch (IllegalArgumentException ex) {
// 丟擲異常...
}catch (InvocationTargetException ex) {
// 丟擲異常...
}
}
處理器呼叫
接下里分析下 handleInternal 方法中的處理器呼叫
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 1.資料繫結
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 2.建立 ModelFactory
// 添加了 @ModelAttribute,@SessionAttributes 等註解內容
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 3.建立 ServletInvocableHandlerMethod,並繫結相關屬性
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 4.建立 ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 省略部分原始碼...
// 5.呼叫控制器方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 6.返回 ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
接著來看該類的 getModelAndView 方法:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory,
NativeWebRequest webRequest) throws Exception {
// 更新 Mdoel
modelFactory.updateModel(webRequest, mavContainer);
// 判斷請求是否處理器完畢
if (mavContainer.isRequestHandled()) {
return null;
}
// 關鍵 -> 建立 ModelAndView
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
// 判斷 view 是不是字串
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
// 重定向相關
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
總結
下面來看 HandlerAdapter 的工作流程圖: