這一次搞懂SpringMVC原理說(shuō)明
前言
前面幾篇文章,學(xué)習(xí)了Spring IOC、Bean實(shí)例化過(guò)程、AOP、事務(wù)的源碼和設(shè)計(jì)思想,了解了Spring的整體運(yùn)行流程,但如果是web開(kāi)發(fā),那么必不可少的還有Spring MVC,本篇主要分析在請(qǐng)求調(diào)用過(guò)程中SpringMVC的實(shí)現(xiàn)原理,通過(guò)本篇要搞懂它是怎么解決請(qǐng)求、參數(shù)、返回值映射等問(wèn)題的。
正文
請(qǐng)求入口
我們都知道前端調(diào)用后端接口時(shí),都會(huì)通過(guò)Servlet進(jìn)行轉(zhuǎn)發(fā),而Servlet的聲明周期包含下面四個(gè)階段:
實(shí)例化(new)
初始化(init)
執(zhí)行(service調(diào)用doGet/doPost)
銷(xiāo)毀(destroy)
前兩個(gè)階段在Spring啟動(dòng)階段就做好了(init根據(jù)配置可能是第一次請(qǐng)求時(shí)才會(huì)調(diào)用),銷(xiāo)毀是服務(wù)關(guān)閉的時(shí)候進(jìn)行,本文主要分析的就是請(qǐng)求執(zhí)行階段。我們知道SpringMVC的核心就是DispatcherServlet,該類(lèi)是對(duì)Servlet的擴(kuò)展,所以直接從該類(lèi)的service方法開(kāi)始,但在此類(lèi)中沒(méi)有service方法,那肯定是在其父類(lèi)中,我們先來(lái)看看其繼承體系:

逐個(gè)往上找,在FrameworkServlet方法中就有一個(gè)service方法:
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
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) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
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 {
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);
}
}
但其主要還是調(diào)用父類(lèi)HttpServlet中的方法,而該類(lèi)又會(huì)根據(jù)不同的請(qǐng)求方式會(huì)調(diào)到子類(lèi)中,最后的核心方法就是DispatcherServlet中的doDispatch方法:
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);
//這個(gè)方法很重要,重點(diǎn)看
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//獲取跟HandlerMethod匹配的HandlerAdapter對(duì)象
// 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//前置過(guò)濾器,如果為false則直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//調(diào)用到Controller具體方法,核心方法調(diào)用,重點(diǎn)看看
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//中置過(guò)濾器
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);
}
//視圖渲染及后置過(guò)濾器執(zhí)行
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);
}
}
}
}
MVC的所有處理邏輯都在這個(gè)方法中,先總結(jié)一下這個(gè)方法的實(shí)現(xiàn)邏輯,首先根據(jù)請(qǐng)求的url拿到緩存中的HandlerMethod對(duì)象和執(zhí)行鏈對(duì)象,HandlerMethod中封裝了controller對(duì)象、方法對(duì)象和方法參數(shù)等信息,執(zhí)行鏈則是包含了一個(gè)個(gè)HandlerInterceptor攔截器;然后再通過(guò)HandlerMethod拿到對(duì)應(yīng)的HandlerAdapter,這個(gè)對(duì)象的作用就是去適配我們的controller;準(zhǔn)備工作做完后,首先會(huì)執(zhí)行前置過(guò)濾,如果被攔截則直接返回,否則就去調(diào)用controller中的方法執(zhí)行我們的業(yè)務(wù)邏輯并返回一個(gè)ModelView對(duì)象;接著執(zhí)行中置過(guò)濾器,以及處理全局異常捕獲器捕獲到異常;最后進(jìn)行視圖渲染返回并執(zhí)行后置過(guò)濾器進(jìn)行資源釋放等工作。
以上就是MVC的整體執(zhí)行流程,下面就逐個(gè)來(lái)分析,首先進(jìn)入getHandler方法:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//handlerMappering實(shí)例
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//獲取HandlerMethod和過(guò)濾器鏈的包裝類(lèi)
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
是委托給HandlerMapping對(duì)象的,這是一個(gè)接口,主要的實(shí)現(xiàn)類(lèi)是RequestMappingHandlerMapping,同樣先來(lái)看看其繼承體系:

這個(gè)類(lèi)是管理請(qǐng)求和處理類(lèi)之間的映射關(guān)系的,你是否疑惑它是在哪里實(shí)例化的呢?下面先來(lái)看看MVC組件的初始化。
組件初始化
這里我以自動(dòng)化配置的注解方式說(shuō)明,Spring提供了一個(gè)@EnableWebMvc,通過(guò)前面的學(xué)習(xí)我們知道在這個(gè)注解中必定導(dǎo)入了一個(gè)配置類(lèi),點(diǎn)進(jìn)去可以看到是DelegatingWebMvcConfiguration,這個(gè)類(lèi)就是負(fù)責(zé)MVC的組件和擴(kuò)展實(shí)現(xiàn)的初始化,其本身我們先不看,先看其父類(lèi)WebMvcConfigurationSupport,這個(gè)類(lèi)我們應(yīng)該不陌生,要做一些自定義擴(kuò)展時(shí)就需要繼承該類(lèi)(如攔截器Interceptor),同樣作用的類(lèi)還有WebMvcConfigurerAdapter,這個(gè)類(lèi)是對(duì)前者相對(duì)安全的擴(kuò)展,為什么是相對(duì)安全呢?因?yàn)槔^承前者會(huì)導(dǎo)致自動(dòng)配置失效,而使用后者則不必?fù)?dān)心此問(wèn)題,只需要在類(lèi)上加上@EnableWebMvc注解。
在WebMvcConfigurationSupport中我們可以看到很多@Bean標(biāo)注的方法,也就是mvc組件的實(shí)例化,這里主要看看requestMappingHandlerMapping,其余的可自行閱讀理解,也就是一些Bean的注冊(cè):
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
......省略
return mapping;
}
這里主要看getInterceptors方法如何獲取攔截器的:
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
//鉤子方法,需要自己定義
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
第一次進(jìn)來(lái)會(huì)調(diào)用addInterceptors添加攔截器,這是一個(gè)模板方法,在子類(lèi)DelegatingWebMvcConfiguration中實(shí)現(xiàn):
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
可以看到最終是調(diào)用WebMvcConfigurer的addInterceptors方法,也就是我們對(duì)WebMvcConfigurerAdapter的自定義擴(kuò)展。看到這里我們應(yīng)該明白了MVC的組件是如何添加到IOC容器中的,但是DispatcherServlet又是怎么獲取到它們的呢?回到之前的代碼中,在DispatcherServlet這個(gè)類(lèi)中有一個(gè)onRefresh方法,這個(gè)方法又調(diào)用了initStrategies方法完成了MVC九大組件的注冊(cè):
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
以initHandlerMappings為例,其它組件實(shí)現(xiàn)邏輯基本一樣。首先從IOC容器中拿到handlerMappings的所有實(shí)現(xiàn)類(lèi)(WebMvcConfigurationSupport中注入的對(duì)象就在這里被獲取到),若沒(méi)有,則從DispatcherServlet.properties配置文件中(這個(gè)配置在spring-webmvc工程下org/springframework/web/servlet/DispatcherServlet.properties)獲取默認(rèn)的配置:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
但是onRefresh又是在什么時(shí)候調(diào)用的呢?有兩個(gè)地方,一個(gè)是Servlet初始化時(shí)會(huì)調(diào)用到initWebApplicationContext進(jìn)行容器的初始化,這個(gè)方法中就會(huì)觸發(fā)onRefresh;另外還有一個(gè),在FrameworkServlet中有一個(gè)onApplicationEvent方法,而這個(gè)方法又會(huì)被內(nèi)部類(lèi)ContextRefreshListener調(diào)用,這個(gè)類(lèi)實(shí)現(xiàn)了ApplicationListener接口,表示會(huì)接收容器刷新事件。
以上就就是MVC HandlerMapping組件的初始化邏輯,其它組件實(shí)現(xiàn)邏輯相同,下面不再分析。
調(diào)用Controller
回到getHandler方法,其調(diào)用的是AbstractHandlerMapping類(lèi)的方法:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根據(jù)請(qǐng)求的uri拿到對(duì)應(yīng)的HandlerMethod對(duì)象
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 = obtainApplicationContext().getBean(handlerName);
}
//獲取HandlerMethod和過(guò)濾器鏈的包裝類(lèi)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
//是否是跨域請(qǐng)求,就是查看request請(qǐng)求頭中是否有Origin屬性
if (CorsUtils.isCorsRequest(request)) {
//自定義的鉤子方法獲取跨域配置
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
//注解獲取跨域配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
//這里設(shè)置了跨域的過(guò)濾器CorsInterceptor
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
先看AbstractHandlerMethodMapping.getHandlerInternal:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//從request對(duì)象中獲取uri,/common/query2
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
//根據(jù)uri從映射關(guān)系中找到對(duì)應(yīng)的HandlerMethod對(duì)象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//把Controller類(lèi)實(shí)例化
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根據(jù)url拿到對(duì)應(yīng)的RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
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));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
//如果兩個(gè)RequestMappinginfo什么都相同,報(bào)錯(cuò)
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
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) {
// 拿到匹配的RequestMappingInfo對(duì)象,有可能url相同,@RequestMapping的屬性(請(qǐng)求方式、參數(shù)等)匹配不上
T match = getMatchingMapping(mapping, request);
if (match != null) {
//RequestMappingInfo對(duì)象和HandlerMethod對(duì)象封裝到Match對(duì)象中,其實(shí)就是注解屬性和Method對(duì)象的映射
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
這里邏輯很簡(jiǎn)單,就是通過(guò)請(qǐng)求url從urlLookup中拿到對(duì)應(yīng)的RequestMappingInfo(每一個(gè) @RequestMapping對(duì)應(yīng)一個(gè)RequestMappingInfo對(duì)象)對(duì)象,再根據(jù)RequestMappingInfo對(duì)象從mappingLookup拿到對(duì)應(yīng)的HandlerMethod并返回。
但這里你可能會(huì)比較好奇urlLookup和mappingLookup從哪里來(lái)的,仔細(xì)觀察你會(huì)發(fā)現(xiàn)當(dāng)前這個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)接口InitializingBean,實(shí)現(xiàn)了這個(gè)接口的類(lèi)會(huì)在該類(lèi)的Bean實(shí)例化完成后調(diào)用afterPropertiesSet方法,上面的映射關(guān)系就是在這個(gè)方法中做的。實(shí)際上這個(gè)方法不止完成了上面兩個(gè)映射關(guān)系,還有下面兩個(gè):
corsLookup:handlerMethod -> corsConfig
registry:RequestMappingInfo -> MappingRegistration(包含url、handlerMethod、RequestMappingInfo、name等信息)
這里就不展開(kāi)分析了,奉上一張時(shí)序圖,讀者可根據(jù)下面的時(shí)序圖自行分析:

拿到HandlerMethod對(duì)象后,又會(huì)通過(guò)getHandlerExecutionChain方法去獲取到所有的HandlerInterceptor攔截器對(duì)象,并連同HandlerMethod對(duì)象一起封裝為HandlerExecutionChain。之后是獲取跨域配置,這里不詳細(xì)分析。
拿到HandlerExecutionChain對(duì)象后返回到doDispatch方法,又調(diào)用了getHandlerAdapter
方法拿到HandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//根據(jù)handlerMethod對(duì)象,找到合適的HandlerAdapter對(duì)象,這里用到了策略模式
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
這里的handlerAdapters變量值從哪里來(lái)?相信不用我再分析,主要看這里的設(shè)計(jì)思想,典型的策略模式。
之后調(diào)用完前置過(guò)濾器后,才是真正調(diào)用我們controller方法的邏輯,通過(guò)HandlerAdapter.handle去調(diào)用,最終會(huì)調(diào)用到ServletInvocableHandlerMethod.invokeAndHandle:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//具體調(diào)用邏輯,重點(diǎn)看
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);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//返回值處理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
這個(gè)方法里面主要看invokeForRequest和handleReturnValue的調(diào)用,前者是完成參數(shù)綁定并調(diào)用controller,后者則是對(duì)返回值進(jìn)行處理并封裝到ModelAndViewContainer中。先來(lái)看invokeForRequest:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//獲取參數(shù)數(shù)組
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
doInvoke就是完成反射調(diào)用,主要還是看參數(shù)綁定的實(shí)現(xiàn)邏輯,在getMethodArgumentValues方法中:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
if (ObjectUtils.isEmpty(getMethodParameters())) {
return EMPTY_ARGS;
}
//入?yún)⒌陌b類(lèi),里面包裝了參數(shù)類(lèi)型,參數(shù)名稱(chēng),參數(shù)注解等等信息
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//設(shè)置參數(shù)名稱(chēng)解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//典型的策略模式,根據(jù)parameter能否找到對(duì)應(yīng)參數(shù)的處理類(lèi),能找到就返回true
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//具體參數(shù)值解析過(guò)程,重點(diǎn)看看
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled..
if (logger.isDebugEnabled()) {
String error = ex.getMessage();
if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, error));
}
}
throw ex;
}
}
return args;
}
參數(shù)、返回值解析
因?yàn)閰?shù)類(lèi)型非常多,同時(shí)還會(huì)伴隨各種注解,如:@RequestBody、@RequestParam、@PathVariable等,所以參數(shù)解析的工作是非常繁雜的,同時(shí)還要考慮到擴(kuò)展性,所以SpringMVC依然采用了策略模式來(lái)完成對(duì)各種參數(shù)類(lèi)型的解析綁定,其頂層接口就是HandlerMethodArgumentResolver,而默認(rèn)SpringMVC提供的解析方式就高達(dá)20多種:

上面是類(lèi)圖,讀者可根據(jù)自己熟悉的參數(shù)類(lèi)型找到對(duì)應(yīng)的類(lèi)進(jìn)行分析,最核心的還是要掌握這里的設(shè)計(jì)思想。
接著方法調(diào)用完成后就是對(duì)返回值的處理,同樣的,返回值類(lèi)型也是非常多,也可以使用各種注解標(biāo)注,所以也是使用策略模式實(shí)現(xiàn),其頂層接口是HandlerMethodReturnValueHandler,實(shí)現(xiàn)類(lèi)如下:

調(diào)用完成之后就是執(zhí)行后續(xù)操作了:執(zhí)行中置過(guò)濾器、處理全局異常、視圖渲染以及執(zhí)行后置過(guò)濾器,這些與主流程沒(méi)有太大關(guān)系,本篇不展開(kāi)分析了,最后是MVC的執(zhí)行時(shí)序圖:

總結(jié)
本篇是Spring核心原理系列的最后一篇,前前后后花了一個(gè)月時(shí)間,終于從宏觀上大致上理解了Spring的實(shí)現(xiàn)原理和運(yùn)行機(jī)制,明白了之前項(xiàng)目中一些坑是如何產(chǎn)生的,最主要的是學(xué)到設(shè)計(jì)模式的運(yùn)用以及如何利用Spring的一些常用的擴(kuò)展點(diǎn)進(jìn)行自定義擴(kuò)展。但對(duì)于Spring這個(gè)龐大的體系來(lái)說(shuō),還有很多是要去理解學(xué)習(xí)的,尤其是設(shè)計(jì)思想,只有長(zhǎng)期琢磨才能深刻的理解掌握。在我之前的文章中包括本篇還有很多沒(méi)分析到的細(xì)節(jié),在后面我會(huì)不定期分享出來(lái)。希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Maven中optional和scope元素的使用弄明白了嗎
這篇文章主要介紹了Maven中optional和scope元素的使用弄明白了嗎,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
springboot項(xiàng)目Redis統(tǒng)計(jì)在線用戶(hù)的實(shí)現(xiàn)示例
最近做個(gè)項(xiàng)目需要統(tǒng)計(jì)在線用戶(hù),本文主要介紹了springboot項(xiàng)目Redis統(tǒng)計(jì)在線用戶(hù)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
Spring security BCryptPasswordEncoder密碼驗(yàn)證原理詳解
這篇文章主要介紹了Spring security BCryptPasswordEncoder密碼驗(yàn)證原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
java基礎(chǔ)知識(shí) super和this使用解析
這篇文章主要介紹了java基礎(chǔ)知識(shí) super和this使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Springboot并發(fā)調(diào)優(yōu)之大事務(wù)和長(zhǎng)連接
這篇文章主要介紹了Springboot并發(fā)調(diào)優(yōu)之大事務(wù)和長(zhǎng)連接,重點(diǎn)分享長(zhǎng)事務(wù)以及長(zhǎng)連接導(dǎo)致的并發(fā)排查和優(yōu)化思路和示例,具有一定的參考價(jià)值,感興趣的可以了解一下2022-05-05
Myeclipse鏈接Oracle等數(shù)據(jù)庫(kù)時(shí)lo exception: The Network Adapter coul
今天小編就為大家分享一篇關(guān)于Myeclipse鏈接Oracle等數(shù)據(jù)庫(kù)時(shí)lo exception: The Network Adapter could not establish the connection,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03

