前言
上一節,我們隻是從總體上分析了springMVC的請求處理流程,但針對每一步的細節,具體還有哪些操作并沒有說明,這一節,就請求過程中的很一步處理給出相應的分析說明。
源碼分析
假設在伺服器部署了一個工程project,其中有一個業務是根據使用者ID擷取使用者的詳細資訊,GET請求的URL是:http://localhost:8080/project/userController/getUserInfo?id=10001(不考慮有其它伺服器轉發),當伺服器接收到浏覽器發送的這個請求後,按httpServlet的規範,首先會找到DispatcherServlet中的doGet方法,發現此方法在DispatcherServlet的父類FrameworkServlet中
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
發現它直接委托給了processRequest去執行(其它的方法,如:doPost,doPut等也都是委托給這個方法去處理),我們來看看processRequest方法内容
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//先取上一次的本地上下文,其實就是取Locale的資訊,比如:國家,語言等等
//主要是緩存,以便後面的恢複
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立一個LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//取上一個請求的RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//建立一個RequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//異步管理類,這個是内部使用的類,在這裡不做分析
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
/**初始化ContextHolder,其實就是把目前的requestAttributes放到
ThreadLocal中,友善在一些沒有request作為參數的方法中,
可以直接使用 RequestContextHolder.currentRequestAttributes()
取到目前的request或response對象
*/
initContextHolders(request, localeContext, requestAttributes);
try {
//重點,真正的處理交給了這個方法去執行
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//重置成上一個請求
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//釋出事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
我們找到doService,發現它是一個FrameworkServlet的抽象方法,也就是說它希望子類DispatcherServlet去實作具體的處理過程
protected abstract void doService(HttpServletRequest request,
HttpServletResponse response) throws Exception;
在DispatcherServlet中的doService方法實作
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
//如果此次請求中包含一個uri請求,就儲存request的一個快照
//友善恢複request的原始屬性
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
//設定一些全局參數給request
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//重點,主體處理又交給了這個方法
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
找到doDispatch方法,此時才是直接的開始
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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;
Exception dispatchException = null;
try {
//檢查是否是Multipart請求,比如:檔案上傳就是一個Multipart請求
//否則傳回原來的request
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//根據request去handlerMapping映射中到對應的HandlerExecutionChain
//
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果沒有找到,說明請求的位址錯誤
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//取到執行鍊後,再去找對應的處理器的擴充卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 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;
}
}
//執行鍊中的攔截器執行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//交給擴充卡去解析參數等等并執行對應的方法,傳回
//ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果沒有view就給它一個預設的
applyDefaultViewName(processedRequest, mv);
//執行攔截器的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//處理傳回結果,這裡就是把Model填充到view中
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);
}
}
}
}
checkMultipart處理的内容很簡單,上面已經說明,我們來看看 getHandler(processedRequest)做了哪些事件?主要是周遊handlerMappings找到比對的執行鍊
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
HandlerMapping中的getHandler方法
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//委托給此方法去查找handler
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
進入到AbstractHandlerMethodMapping的getHandlerInternal方法,先從request擷取到lookupPath,相當于上面的(userControoler/getUserInfo)
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
/**
這裡的mappingRegistry,其實就是一個映射關系注冊中心,維護所有mapping與handlerMethod的映射關系
為了提供并發通路,每次都是顯示的擷取鎖,擷取後再顯示的釋放鎖
*/
this.mappingRegistry.acquireReadLock();
try {
//擷取handlerMethod,下面進行分析
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//從mappingRegistry中的urllookup中擷取RequestMappingInfo集合
//其實,每一個requestMapping都會被封裝成RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//周遊所有的比對的mappings(directPathMatches)
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
//排序找到最比對的那個,也就是第一個
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
//取最比對的match
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
//取第二比對的
Match secondBestMatch = matches.get(1);
//如果兩個相同,則springmvc也不知道哪個是使用者需要的方法了
//是以,不能存在相同的路徑對應兩個方法
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
//傳回最佳比對的mapping對應的HandlerMethod
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
//找到比對的mapping加入到matchs集合中
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
Match類的結構,它其實是AbstractHandlerMethodMapping的内部類
/**
* A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of
* comparing the best match with a comparator in the context of the current request.
*/
private class Match {
//Match類是AbstractHandlerMethodMapping的内部類
private final T mapping;
private final HandlerMethod handlerMethod;
public Match(T mapping, HandlerMethod handlerMethod) {
this.mapping = mapping;
this.handlerMethod = handlerMethod;
}
@Override
public String toString() {
return this.mapping.toString();
}
}
再回到AbstractHandlerMethodMapping的getHandlerInternal方法中,接下來是判斷handlerMethod是否為空,如果不為空則調用createWithResolvedBean()方法,這個方法的邏輯是如果目前handlerMethod中的bean隻是名稱時,那麼通過beanFactory獲得bean的執行個體,再次封裝成HadnerMethod對象傳回。這裡的bean是指方法所在的類對象,例如:上述中的userController中的getUserInfo方法,getUserInfo就是handlerMethod而userController就是上面所說的bean。
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
再回到AbstractHandlerMapping的getHandler(HttpServletRequest request)方法,接下來就是執行
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
擷取執行鍊,進入此方法,
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//先判斷是否是執行鍊對象,如果不是就把handler封裝成一個執行鍊
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//周遊所有的攔截器加入到執行鍊中
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
到此時,我們已經擷取到了HandlerExecutionChain執行鍊對象,傳回到DispatcherServlet中。接下來就是需要擷取執行擴充卡(管家)
總結
重新回顧一下,在DispatcherServlet的doDispatch方法中,第一步是判斷是否是Multipart請求,第二步就是擷取執行連結對象mappedHandler。分析到此也隻是完成這個方法的第二步,即通過請求的url找到對應的handlerMethod和bean對象,近而取得了執行鍊對象。為下一步的執行做準備。