在SpringMvc啓動以及加載過程中以及講到Spring啓動加載過程,現在看下以及請求進來的過程以及數據流轉過程,首先在看源碼之前簡單對SpringMvc請求過程有一個簡單的大方向的瞭解:
第一步就是獲取,然後在Web Spring獲取後根據request 中url來從HandlerMap查詢Handler
第二bu
- 請求數據經過DispatchServerlet
- 通過request 獲取url查詢Handler
- Handler調用相應的 Controller方法獲取結果
- 組裝成ModelAndView
- ModelAndView解析成view
- 返回給客戶端
可以看到DispatchServlet是控制整過的控制者,以及邏輯分發者接下在再看下該類的UML圖,可以看到這個類主要是Spring以及Servlet的一個橋樑既有實現接口Servlet相關的接口,也有Spring相關的接口,一開始主要這裏主要涉及Servlet接口相關的。從UML圖可以看到它是HttpServlet的子類,那麼他有一個很重要的一個性質接受用戶的請求,web配置的URL請求攔截的請求都會通過該類的
void service(HttpServletRequest req, HttpServletResponse resp)
方法處理。從代碼上看,這個類的service方法主要通過父類FrameWorkServlet實現:
FrameWorkServlet.service:
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//獲取請求方法:POST GET PUT 等
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
//調用HttpServlet service方法
super.service(request, response);
}
}
再看下父類HTTPServlet的方法:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//獲取不同的請求方法類型
String method = req.getMethod();
//根據不同的請求類型走不同的方法
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
這塊代碼功能很簡單就是獲取請求的 方法類型,不同的請求類型走不同的邏輯,這些可以交給子類實現,這裏用了模板模式,在FrameServlet重寫了些 doGet doPost方法:
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
每一個方法最後都會調用processRequest這個方法,而這個方法最核心的是調用了
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
這個方法被DispatchServlet實現了,也就是說:一個請求過來最終的公共邏輯會走到DispatchServlet#doService方法。
接下來就是真正的邏輯開始了。這個方法看代碼其實最核心的調用
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
所以我們直接從這個方法開始看:
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 {
//將請求轉化解析,如果是文件上傳的請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//獲取處理Handler(重點)
mappedHandler = getHandler(processedRequest);
//沒有找到Handler 說明404啦
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;
}
}
//這裏執行所有的和這個請求有關的攔截器,執行preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//適配器調用Handler方法,處理請求以及Handler,獲取modelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//這裏執行所有的和這個請求有關的攔截器,執行postHandle方法
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", 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);
}
}
}
}
這裏我們重點關注一下幾個過程:
- 獲取MapHandler過程,以及這個對象包含的信息
- HandlerAdapter執行過程
- HandlerAdapter角色作用
接下來我看從這三個方面來看下,下面都是基於註解的方式來走的(@Controller @RequestMapping)
MapHandler的獲取
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() + "'");
}
//從HandlerMapping中獲取Handler
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
在this.handlerMappings代表配置不同的類型,我們直接看debug的截圖:
可以看到有三個HandlerMapping,其中SimpleUrlHandlerMapping是配置加載的,BeanNameURLHandlerMapping是默認實現的,RequestMappingHandlerMapping是基於@Controller @RequestMapping實現的。所以我們只用了註解實現的,我們只關心這塊邏輯就好。接下來看下Mapping的getHandler方法:
@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);
}
//獲取這個請求相關的攔截器,並且和Handler組裝成一個對象
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;
}
以上代碼只完成兩個事情:獲取一個請求相關的Handler 以及攔截器,看下這兩個方法:
獲取Handler
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//獲取請求URL
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
//加一個共享鎖
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 + "]");
}
}
//生成創建Handler
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//獲取一個Match對象,在下面看下這個debug數據內容(標註1)
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//把可以和這個匹配的Match對象加入一個List中(標註2)
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()) {
//把剛剛的Matches排序(標註3)
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);
//有兩個一樣Match,那麼就會拋出異常
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);
//返回Match的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) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
//將拿到的match和mapping包裝成一個對象
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
上面兩個方法主要獲取Handler的過程:
- 首先獲取T泛型集合(這個T是一個啥)
- 將Match集合排序獲取最優的(是啥排序策略)
- 返回最優Match.Handler(這個又是一個啥)
上面我們知道了這個過程,但是對裏面的數據卻沒有一個感性的認識,現在直接看debug數據就行。
首先看下標註1,獲取一個List,其實是從this.urlLookup這個Map中獲取的:
這個數據結構內容是,key放的是我們的Url,value是一個list,list的每一個對象是怎麼來的呢?其實這個數據來源就是註解@RequestMapping中各個轉化的內容,也就是一個出現多個一樣的的Url的處理方法(後面會有如何選其中的一個方法)。回答第一個問題,T是一個包含了註解信息的對象,包括 url Post/Get 等
然後就到了註解2,把我們剛剛拿到的所有的註解的信息會構造一個Match,拿着之前註解信息還會從this.mappingRegistry獲取一個Handler。
我們可以看到獲取的value是一個HandlerMethod對象,它是記錄的主要是方法的信息,例如這個url對應的Controller bean Method,以及方法的參數信息(第三個對象答案)。也就是一個Match對象有一個@RequestMapping註解信息以及方法的信息
接下來看下標註3,標註3主要對多個Match進行選取最優的Match,有兩個最優的那麼就會拋異常,我們從代碼獲取比較器一步步走進去我們可以看到最終是按照@RequestMapping信息對象(RequestMappingInfo)來排序的(上面第二個問題的答案),,這裏就不詳細看了,裏面代碼比較簡單。
以上就是獲取Handler的邏輯:
- 根據URL獲取註解的信息對象
- 根據註解信息對象獲取具體的方法信息對象
- 根據註解信息對象排序選出最優的哪一個方法信息Handler 返回,反之就會拋異常
HandlerInterceptor獲取
在獲取完Handler後獲取攔截器,組裝成 HandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
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;
//攔截器可以匹配URL,就會加入到鏈表中
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
HandlerAdapter
我們在上面一部已經獲取了URL對應的Handler,裏面主要的要的處理的方法信息,這個請求交給哪一個Controller的哪一個方法,現在HandlerAdapter其實起的是一個執行的過程,以及提供各種上下文的對象完成數據映射轉換,並且調用方法完成業務邏輯。
/**
* 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 + "]");
}
//獲取可以支持的適配器,這個和controller配置有關係,註解實現的返回RequestMappingHandlerAdapter
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");
}
這裏按照註解實現那麼就會返回RequestMappingHandlerAdapter,從主流程handle->handleInternal->invokeHandlerMethod方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);//構建執行器
//設置請求參數到Controller轉換對象
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;
}
//生成modelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
上面的流程就是通過Handler以及上下文一些工具對象(各種Convert類)構建一個新的執行類進行執行。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//執行獲取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
//對返回的參數進行轉換寫入到Response中
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;
}
}
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//轉化獲取controller需要的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
//通過反射調用bean的處理方法,拿到返回值
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
上面的邏輯主要分爲兩步:
- 獲取controller方法執行的參數
- 通過反射執行方法
接下來看下參數的獲取過程:
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//獲取controller參數信息
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);
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("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
這一步主要識別一些根據參數以及標記例如註解等來拿到 HandlerMethodArgumentResolver 對象,例如參數帶了 @RequestJsonbody 會有一個專門的類RequestResponseBodyMethodProcessor 來獲取參數,帶了@RequestParam 會有RequestParamMethodArgumentResolver類來獲取參數。
還要一部就是在拿到返回值對返回值也有一個類似的轉化的流程在
invokeAndHandle->this.returnValueHandlers.handleReturnValue
中,會調用
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("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;
}
這裏如果通過拿到返回值得信息,例如註解,類等信息,決定拿到哪一個
HandlerMethodReturnValueHandler
來處理返回值。
以上就是主要的流程,回顧一下:
- 獲取Servlet請求
- 獲取請求 Url獲取具體符合的方法集合
- 選取最優的方法
- 獲取轉換的Controller方法的執行參數
- 反射執行
- 獲取結果,並且轉換設置到Response中