本文涉及到代碼比較多,建議在電腦端閱讀!!!web
借用網上的一張springMVC流程圖,spring
spingMVC源碼分三部分來分析設計模式
1:ApplicationContext初始化時創建全部url和controller類的對應關係(使用map保存)數組
2:根據請求url找到對應的controller,並從controller中找處處理請求的方法mvc
3:request參數綁定到方法的形參,執行方法並處理請求,最後返回結果app
咱們首先看第一個步驟,也就是創建Map<url,controller>關係的部分.ide
第一部分的入口類爲this
ApplicationObjectSupporturl
--->setApplicationContext()spa
-->initApplicationContext(context),實現子類AbstractDetectingUrlHandlerMapping實現了該方法,因此咱們直接看子類中的初始化容器
創建當前ApplicationContext中的全部controller和url的對應關係
/**
*獲取controller中全部方法的url,由子類實現,典型的模板模式
**/
protected abstract String[] determineUrlsForHandler(String beanName);
determineUrlsForHandler(String beanName)方法的做用是獲取每一個controller中的url,不一樣的子類有不一樣的實現,這是一個典型的模板設計模式(前面有一篇文章有講到模板模式),
DefaultAnnotationHandlerMapping是AbstractDetectingUrlHandlerMapping的子類,處理註解形式的url映射.因此咱們這裏以DefaultAnnotationHandlerMapping來進行分析,
接下來看看DefaultAnnotationHandlerMapping是如何查beanName上全部映射的url.
/**
* 獲取controller中全部的url
*/
protected String[] determineUrlsForHandler(String beanName) {
// 獲取ApplicationContext容器
ApplicationContext context = getApplicationContext();
//從容器中獲取controller
Class<?> handlerType = context.getType(beanName);
// 獲取controller上的@RequestMapping註解
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
if (mapping != null) { // controller上有註解
this.cachedMappings.put(handlerType, mapping);
// 返回結果集
Set<String> urls = new LinkedHashSet<String>();
// controller的映射url
String[] typeLevelPatterns = mapping.value();
if (typeLevelPatterns.length > 0) { // url>0
// 獲取controller中全部方法及方法的映射url
String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
for (String typeLevelPattern : typeLevelPatterns) {
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}
boolean hasEmptyMethodLevelMappings = false;
for (String methodLevelPattern : methodLevelPatterns) {
if (methodLevelPattern == null) {
hasEmptyMethodLevelMappings = true;
} else {
// controller的映射url+方法映射的url
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
// 保存到set集合中 addUrlsForPath(urls, combinedPattern);
}
}
if (hasEmptyMethodLevelMappings ||
org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
addUrlsForPath(urls, typeLevelPattern);
}
}
// 以數組形式返回controller上的全部url
return StringUtils.toStringArray(urls);
}
else {
// controller上的@RequestMapping映射url爲空串,直接找方法的映射url
return determineUrlsForHandlerMethods(handlerType, false);
}
// controller上沒@RequestMapping註解
} else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
// 獲取controller中方法上的映射url
return determineUrlsForHandlerMethods(handlerType, false);
}
else {
return null;
}
}
上面的方式返回該controller中的全部url,
接下來把urls和controller放在handlerMap中
遍歷式存放for()循環
//具體存放的方法
protected void registerHandler(String urlPath, Object handler) {// Eagerly resolve handler if referencing singleton via name.
//若是經過名稱引用單體,則急於解析處理程序。(感受翻譯的不非常準確) if (!this.lazyInitHandlers && handler instanceof String) {if (getApplicationContext().isSingleton(handlerName)) {this.handlerMap.get(urlPath);
//若是存在重複的url,則報異常IllegalStateExceptionif (mappedHandler != null) {if (mappedHandler != resolvedHandler) { throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");else {//不然url校驗if (urlPath.equals("/")) { else if (urlPath.equals("/*")) { else {
//放到handlerMap中this.handlerMap.put(urlPath, resolvedHandler);
其中存放url和controller的map:
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
看完以上源碼我們能夠知道Map<urls,Controller>關係以及創建好了(HandlerMapping關係)
前面我們已經講過了DispatcherServlet爲核心代碼(入口),輕重黑心的方法爲doService()
,另外doService()中的核心邏輯由doDispatch()實現,咱們查看doDispatch()的源代碼.
org.springframework.web.servlet.DispatcherServlet
/** * 中央控制器,控制請求的轉發 */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {null;int interceptorIndex = -1;try {boolean errorView = false;try {// 1.檢查是不是文件上傳的請求 processedRequest = checkMultipart(request);// 2.取得處理當前請求的controller,這裏也稱爲hanlder,處理器,第一個步驟的意義就在這裏體現了.這裏並非直接返
回controller,而是返回的HandlerExecutionChain請求處理器鏈對象,該對象封裝了handler和interceptors. mappedHandler = getHandler(processedRequest, false);// 若是handler爲空,則返回404 if (mappedHandler == null || mappedHandler.getHandler() == null) {return;//3. 獲取處理request的處理器適配器handler adapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 處理 last-modified 請求頭 String method = request.getMethod();boolean isGet = "GET".equals(method);if(isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());urlPathHelper.getRequestUri(request);if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;// 4.攔截器的預處理方法 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();if (interceptors != null) {for (int i = 0; i < interceptors.length; i++) {if(!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {null);return;// 5.實際的處理器處理請求,返回結果視圖對象 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//結果視圖對象的處理 if (mv != null && !mv.hasView()) {// 6.攔截器的後處理方法 if (interceptors != null) {for (int i = interceptors.length - 1; i >= 0; i--) {catch (ModelAndViewDefiningException ex) {catch (Exception ex) {null ? mappedHandler.getHandler() : null);null);if (mv != null && !mv.wasCleared()) {if (errorView) {clearErrorRequestAttributes(request);else {/* logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() "': assuming HandlerAdapter completed request handling");*/ }//請求成功響應以後的方法 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);catch (Exception e){// logger.error("errpr".e); }
上面getHandler(processedRequest, false)方法:
本方法方法實際上就是從HandlerMapping中找到url和controller的對應關係.這也就是第一個步驟:創建Map<url,Controller>的意義.咱們知道,最終處理request的是controller中的方法,咱們如今只是知道了controller,還要進一步確認controller中處理request的方法.因爲下面的步驟和第三個步驟關係更加緊密,直接轉到第三個步驟.
上面的doDispatch()方法中,第2步其實就是從第一個步驟中的Map<urls,beanName>中取得controller,而後通過攔截器的預處理方法,到最核心的部分--第5步調用controller的方法處理請求.
在第2步中咱們已經知道了處理request的Controller類,第5步就是要根據url肯定controller中處理請求的方法,而後經過反射獲取該方法上的註解和參數,解析方法和參數上的註解,最後反射調用方法獲取ModelAndView結果視圖。
由於上面採用註解url形式說明的,因此咱們這裏繼續以註解處理器適配器來講明,
第5步調用的就是AnnotationMethodHandlerAdapter的handle(),該方法中的核心邏輯由invokeHandlerMethod(request, response, handler)實現。
/**
*獲取處理請求的方法,執行並返回結果視圖
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
// 1.獲取方法解析器
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
// 2.解析request中的url,獲取處理request的方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 3.方法調用器
ServletHandlerMethodInvoker methodInvoker=new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
// 4.執行方法
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
// 5.封裝結果視圖
ModelAndView mav =methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
這一部分的核心就在2和4了.先看第2步,經過request找controller的處理方法.實際上就是拼接controller的url和方法的url,與request的url進行匹配,找到匹配的方法.
自此代碼就回到了我們的controller中的方法返回類型ModelAndView了