SpringMVC源码分析
来源:互联网 发布:php base64解码后乱码 编辑:程序博客网 时间:2024/06/06 02:34
1 Servlet功能分析
我们知道,WEB应用一般是打包成WAR包然后部署到Tomcat等应用服务器上去的;部署完成后,通过URL访问时,一般是http://ip:port/app这种访问路径。Tomcat根据路径中端口后的第一个单词并结合该应用的web.xml文件,可以找到哪个应用的哪个Servlet需要处理该URL请求。
在Spring之前,如果要开发一个处理Http请求的功能,需要做两项工作:一是开发继承于Servlet类的业务处理Servlet;二是在Web.xml中注册并映射该Servlet处理的请求路径。
我们先来创建一个简单原始的测试工程(通过Itellij测试),以了解原始Servlet的配置及生效过程,GITHUB地址:https://github.com/icarusliu/learn
1.1 原始的WEB工程
第一步:配置Itellij Tomcat运行环境(Tomcat9):
第二步:通过Itellij创建一个空白工程learn;
第三步:添加Module,名称为testWebApp;
Mvn初始化完成后的工程结构如下所示:
第四步:构建工程,按下图进行:
第五步:运行Tomcat,将会跳转到首页;
第六步:定义测试Servlet
此处使用Kotlin,并引入log4j等日志组件,具体的MVN配置文件见GITHUB工程。
Servlet代码见下文,需要继承自HttpServlet;
class TestServlet: HttpServlet() { val logger: Logger = LoggerFactory.getLogger(TestServlet::class.java) override fun doGet(request: HttpServletRequest, response: HttpServletResponse) { logger.info("Test servlet begin to execute!") response.outputStream.println("TestServlet") logger.info("Test servlet has been executed!") }}
该Servlet实现很简单的向前台返回TestServlet字符串的过程;
此时Servlet定义即完成;但仅仅这样还不够,Tomcat并不知道什么情况下来执行这个Servlet; 这个时候就需要在Web.xml文件中进行Servlet的配置了,具体的配置见下文:
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>test</servlet-name> <servlet-class>com.liuqi.learn.testWebApp.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping></web-app>
以上配置主要包含两个,一是Servlet的定义,名称以及它后台对应的Java类;二是Servlet与URL的映射关系,这里表示将它映射到/test这个地址上。
这个时候我们启动Tomcat,然后打开链接:http://localhost:8080/test,就会看到浏览器输出的了内容为TestServlet的页面。
经过以上几个步骤,一个最原始的不使用任何框架的WEB应用即构建完成。
经过以上分析,WEB应用一般就是一系列Servlet的集合;想象下,如果我们要完成A与B两个功能,那我们就可以定义两个Servlet并分别进行路径映射; 但这样的话功能越多Servlet就会越来越多,配置文件也越来越复杂;如果在此基础上进行抽象,Servlet只定义一个,在Servlet内部再根据URL进行功能分发,这样就会简单很多了。
SpringMVC实际就是这样做的,它定义了一个名称为DispatcherServlet的Servlet,用于接收请求并进行分发处理。
1.2 SpringMVC配置
上文已经分析了,SpringMVC的入口实际就是实现了一个名称为DispatcherServlet的Servlet。
使用Spring而非Spring Boot的实现时,需要在Web.xml中配置该DispatcherServlet;
<web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app-context.xml</param-value> </context-param> <servlet> <servlet-name>app</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping></web-app>
使用Spring Boot的加载过程将会在后续进行分析,此处暂不涉及。
1.3 Itellij查找方法实现过程
在Java类中,可能出现A继承B,B继承C,C继承D;D中有多个方法,有的在C中实现,有的在B,而有的在C;
像这种情况,在A中需要查找某个方法的实现在其父类的哪个类中,可通过Ctrl+F12的快捷键来实现,其界面如下:
如需要在DispatcherServlet中查找Service的实现,我们按快捷键弹出查找窗口后,输入Service,会将名称中包含Service的方法全部查找出来;如果选中了”Inherited members”,则其所有父类中符合条件的方法全部都会搜索到,再鼠标选中记录后就可以方便的跳转到需要查找的方法实现的地方。
如上图中表示Service在FrameworkServlet及HttpServlet中都有实现,通过查看这两个地方可以清楚的知道FrameworkServlet中的实现为最终实现。
这个操作在阅读Spring源码时会经常使用到,后续不再说明。
2 SpringMVC分析
2.1 关键类分析
DispatcherServlet收到请求后,根据它本身所维护的URL路径映射关系,决定调用哪个Controller来进行请求响应;其中涉及到的关键类见下表:
我们先来看下几个关键类在响应请求时是如何相互合作的:
在后面的分析中我们将会看到这个请求响应过程是如何进行的。
2.2 生效过程源码分析
Servlet的关键方法为init与service;Init方法用于初始化ApplicationContext、BeanFactory等对象; 而Service用于响应请求。 我们将针对DispatcherServlet的init与service方法进行分析,以了解SpringMVC的初始化及请求响应过程。
2.2.1 初始化
我们先来看DispatcherServlt的Init方法。
2.2.1.1 init
经过源码阅读,DispatcherServlet的init方法实际在ServletBean中定义,其实现如下:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
实际调用initServletBean方法;该方法在DispatcherServlet的父类FrameworkServlet中定义:
@Overrideprotected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); }}
其关键调用为initWebApplicationContext
2.2.1.2 initWebApplicationContext
该方法主要用于初始化WebApplicationContext,实现BeanFactory的初始化、Bean配置的加载等功能,其实现如下:
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac;}
实际就是对DispatcherServlet的webApplicationContext属性进行配置与刷新;
其关键过程就是如果属性为空时,通过createWebApplicationContext创建属性,此时实例化的实际类名为contextClass属性所指定的类;该属性的初始化是在哪个地方进行的?
跟踪该属性,我们可以看到在FrameworkServlet中有定义:
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
实际DEFAULT_CONTEXT_CLASS的值为: XmlWebApplicationContext.class
也就是,如果没有特殊配置,默认的ContextClass为XmlWebApplicationContext类;
该对象默认从名为/WEB-INF/applicationContext.xml的文件中加载Bean定义;
其refresh方法定义如下:
@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }}
其主要过程:
invokeBeanFactoryPostProcessors: 在Bean初始化前配置BeanFactory中的属性,可修改Bean的定义属性;
registerBeanPostProcessors:从容器中查找BeanPostProcessors的Beans并进行注册;
onRefresh: 初始化Servlet的一些属性;
初始化完成后,如果调用ApplicationContext中的GetBean方法时,实际调用的是BeanFactory中的GetBean方法进行Bean的实例化等过程;关于Bean实例化的具体过程后续再进行分析。
2.2.1.3 onRefresh
我们来重点分析下DispatcherServlet的onRefresh方法,通过阅读源代码,可以发现其调用的实际为initStrategies方法,其实现如下:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context);}
可以看到在这个地方会初始化HandlerMappings、HandlerAdapters、ViewResolvers等属性;
我们先来看下HandlerMapping的初始化:
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<HandlerMapping>(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. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } }}
其执行过程为:如果配置了detectAllHandlerMappings属性,则查找所有类型为HandlerMapping的Bean;如果没有配置则只查找名称为handlerMapping的Bean; 如果两个都没有查找到则使用默认的HandlerMapping类。那默认的Handler在哪进行配置的?
跟踪getDefaultStrategies方法,其逻辑就是从DispatcherServlet.properties文件中查找默认的配置项; 这个文件的内容如下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
可以看到HandlerMapping如果用户没有进行配置的话,则使用默认的BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping两个HandlerMapping。
HandlerAdapter等的初始化过程与HandlerMappings一致,不再赘述。
2.2.2 请求响应
Servlet的请求响应是通过Service方法来实现的;我们先来分析Service方法的实现;
2.2.2.1 Service方法实现分析
我们先来跟踪Service方法的实现,在DispatcherServlet中已经没有没有Service方法定义,查找其父类,查找到最终实现在HttpServlet中,其关键代码段如下:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ... } 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); }}
可见HttpServlet中实际调用的是doGet/doPost等方法;
再跟踪doGet等方法的实现,以doGet为例,实际定义在FrameworkServlet中:
@Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}
实际调用为processRequest方法,其关键代码段如下:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... doService(request, response); ...}
可以看到实际调用的是doService方法,而这个方法的实现就在DispatcherServlet中:
@Overrideprotected 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. 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(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. 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); } } }}
该方法主要是为Request设置了一系列的属性,最终调用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); // Determine handler for the current request. 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. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } 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); } } }}
此处看到,之前所介绍的几个关键类已经开始起作用了;
该方法即为Service生效的关键过程,分析该方法执行流程如下:
接下来我们就上述流程中几个关键的过程进行分析。
2.2.2.2 Handler查找过程
handler查找通过DispatcherServlet中的findHandler方法进行:
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;}
其中,HandlerMappings这个属性的初始化过程在2.2.1.3中已分析,我们知道默认情况下如果用户未配置HandlerMapping类时使用默认的BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping,查看DefaultAnnotationHandlerMapping类,看到这个类已经废弃了,其建议是使用RequestMappingHandlerMapping类,而这个类是注解中主要使用的RequestMapping注解的处理类,因此此处我们主要分析RequestMappingHandlerMapping这个类的实现及生效过程。
我们从其getHandler方法开始分析;其getHandler方法在其父类的AbstractHandlerMapping中实现,其实现如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 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;}
这个方法主要做了两个事情,一是调用getHandlerInternal获取Handler;二是调用getHandlerExecutionChain获取生效的各个拦截器并组装成HandlerExecutionChain并返回;
跟踪getHandlerInternal实现,其实现在类AbstractHandlerMethodMapping中,实际上调用了lookupHandlerMethod方法来获取HandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); 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)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } Match bestMatch = matches.get(0); if (matches.size() > 1) { if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); 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); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); }}
其过程为先从mappingRegistry中获取RequestMappingInfo对象,然后将所有的RequestMappingInfo对象转换成Match对象;如果找到的Match对象列表不为空,则返回其第一个Match元素的HandlerMethod属性;
这个地方逻辑有点绕;我们先来看下mappingRegistry属性,这个属性对象类型为MappingRegistry对象,主要包含了一系列的Map来存储数据。 它的初始化过程在方法initHandlerMethods中进行:
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods());}
实际上就是找出所有满足isHandler方法的所有Bean,检测其所有满足条件的Methods,最终注册到mappingRegistry中去。
isHandler的判断依据,其实现在RequestMappingHandlerMapping中:
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));}
可以看到如果Bean被Controller或者是RequestMapping注解,则认为它是一个Handler。
而detectHandlerMethods方法完成的功能就是检测满足条件的Methods,并将其注册到mappingRegistry属性中去;
至此Handler的查找过程已经分析完毕。
2.2.2.3 HandlerAdapter查找过程
回到doDispatcher方法,在查找Handler完成后即调用getHandlerAdapter(DispatcherServlet)来查找HandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } 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");}
就是从handlerAdapters中查找合适的HandlerAdapter并返回。在2.2.1.3中我们已经分析了handlerAdapters的加载过程,其加载的默认的HandlerAdapter为HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、AnnotationMethodHandlerAdapter
查看AnnotationMethodHandlerAdapter发现其已建议被RequestMappingHandlerAdapter替换。
2.2.2.4 Handler执行过程
查找到HandlerAdapter及Handler后就简单了,最终就是通过HandlerAdapter调用到了Handler的handle方法;最终返回ModelAndView对象。
而前置及后置拦截器也就是在handle方法执行前后执行的。
参考文档:
1 https://docs.spring.io/spring/docs/5.0.2.RELEASE/spring-framework-reference/web.html#mvc-servlet
- SpringMVC源码分析,springMVC原理
- SpringMVC源码分析系列
- SpringMVC源码分析系列
- springmvc源码分析
- SpringMVC源码分析系列
- springMVC源码分析--HandlerMethod
- springMVC源码分析--ModelFactory
- 一步步分析SpringMVC源码
- SpringMVC源码分析
- SpringMVC源码分析系列
- SpringMVC源码分析
- springmvc----源码分析之springmvc执行流程
- SpringMVC加载WebApplicationContext源码分析
- SpringMVC加载WebApplicationContext源码分析
- springmvc 加载WebApplicationContext源码分析
- SpringMVC-前端控制器源码分析
- SpringMVC加载WebApplicationContext源码分析
- SpringMVC加载WebApplicationContext源码分析
- win10+only-cpu利用Caffe框架测试mnist数据集
- 刘光聪 | TensorFlow:揭示多语言编程的奥秘
- 在AndroidStudio下JNI开发的详细步骤
- Tensorflow动态seq2seq使用总结
- 软件开发过程学习笔记之软件开发流程
- SpringMVC源码分析
- 【RCNN系列】【超详细解析】
- android 使用vitamio播放mkv文件实现音轨切换
- Linux 使用scp命令远程拷贝文件
- 职场社交是一个真需求吗?
- Unity Android打包apk安装错误INSTALL_FAILED_CONFLICTING_PROVIDER
- PHP 二进制高精度计算函数
- c++-字符串旋转
- leetcode_40. Combination Sum II ? 待解决