spring框架提供了构建Web应用程序的全功能MVC模块。通过实现servlet接口的DispatcherServlet来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射、视图解析、本地语言、主题解析以及上传下载文件支持。
SpringMVC的配置文件
1)配置web.xml
一个Web中可以没有web.xml文件,它主要用来初始化配置信息:例如welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。但SpringMVC的实现原理是通过Servlet拦截所有URL来达到控制的目的,所以web.xml的配置是必须的。
contextConfigLocation /WEB-INF/applicationContext.xml org.springframework.web.context.ContextLoaderListener dispatcher org.springframework.web.servlet.DispatcherServlet 1 dispatcher / index.jsp
这里面有两个关键的配置
1、contextConfigLocation:Spring容器的所在位置。这个参数是使Web与Spring的配置文件相结合的关键配置
2、DispatcherServlet:包含了SpringMvc的请求逻辑,Spring使用DispatcherServlet类拦截Web请求并进行相应的逻辑处理。
2)创建Spring配置文件applicationContext.xml
这里的配置就是用来存放应用所需的bean配置信息,和普通的ApplicationContext并无不同。
3)servlet的配置文件
默认的文件名就是servlet名+'_servlet.xml'
在Web启动时,服务器会加载对应于Servlet的配置文件,通常我们将Web部分的配置存放于此配置文件中。
容器之间的关系(个人理解)
这三个配置文件实际上对应的都是容器。首先web.xml对应的容器应该是tomcat加载web应用时首先加载的一个web容器。contextConfigLocation对应的是Spring的webApplicationContext,这是一个IOC容器,主要用来控制反转消除依赖用的。servlet对应的是servlet的容器
首先web容器中会通过listener持有IoC容器,这样web容器可以通过持有的IoC容器获取其中的bean。
其次web容器可以存放多个servlet,并不唯一,tomcat初始化servlet容器时,会将IoC容器作为此servlet容器的父容器,子容器将拥有访问父容器对象的权限,而父容器不可以访问子容器的权限。同时将其存放在web容器中。
1、ContextLoaderListener
我们首先先从web.xml开始。在编程方式的时候我们可以直接将配置信息作为参数传入Spring容器中,但在Web下,我们需要通过context-param的方式注册并使用ContextLoaderListener进行监听读取配置
ContextLoaderListener的作用就是启动Web容器的时候,自动装配applicationContext的配置信息。因为它实现了ServletContextListener这个接口,在启动容器的时候会默认执行它实现的方法,通过这个接口,我们可以在应用处理请求之前向servletContext中(也就是web容器)添加任意对象,这个对象在servletContext启动时被初始化,在整个servletContext运行期间都是可见的。
servletContext在启动之后会调用ServletContextListener的contextInitialized方法
public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); }
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!"); } else { Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if(logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if(this.context == null) { this.context = this.createWebApplicationContext(servletContext); } if(this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context; if(!err.isActive()) { if(err.getParent() == null) { ApplicationContext elapsedTime = this.loadParentContext(servletContext); err.setParent(elapsedTime); } this.configureAndRefreshWebApplicationContext(err, servletContext); } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader err1 = Thread.currentThread().getContextClassLoader(); if(err1 == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if(err1 != null) { currentContextPerThread.put(err1, this.context); } if(logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if(logger.isInfoEnabled()) { long elapsedTime1 = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms"); } return this.context; } catch (RuntimeException var8) { logger.error("Context initialization failed", var8); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8); throw var8; } catch (Error var9) { logger.error("Context initialization failed", var9); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9); throw var9; } } }
这个方法做了几件事情
1、WebApplicationContext存在性验证
如果servletContext中已经包含了此WebApplicationContext的话,就会抛出异常
2、创建WebApplicationContext实例,通过createWebApplicationContext方法
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class contextClass = this.determineContextClass(sc); if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); } }
protected Class determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }
首先先确定WebApplicationContext的类型,在determineContextClass方法中可以看到,如果配置中有contextClass时,则使用此类型,若没有,就是用默认的类型,该类型在defaultStrategies下可以找到
我们可以在ClassLoader.properties文件中看到默认类型是XmlWebApplicationContext
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
确定了context的类型后,就会使用反射实例化容器,之后再调用configureAndRefreshWebApplicationContext方法读取容器配置
3、将实例记录在servletContext中
存放在servletContext的property中,对应的键为 WebApplicationContext.class.getName() + ".ROOT"
4、映射当前的类加载器与创建的实例到全局变量currentContextPerThread中
2、DispatcherServlet
servlet在初始化阶段首先会调用其init方法,我们可以再DispatcherServlet的父类HttpServletBean找到方法的实现
public final void init() throws ServletException { if(this.logger.isDebugEnabled()) { this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'"); } HttpServletBean.ServletConfigPropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); if(!pvs.isEmpty()) { try { BeanWrapper ex = PropertyAccessorFactory.forBeanPropertyAccess(this); ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext()); ex.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment())); this.initBeanWrapper(ex); ex.setPropertyValues(pvs, true); } catch (BeansException var4) { if(this.logger.isErrorEnabled()) { this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4); } throw var4; } } this.initServletBean(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully"); } }
1、首先是对配置中的初始化参数进行封装,也就是servlet中配置的<init-param>,将其封装至propertyValue中。
2、将当前的servlet实例转换成beanWrapper实例,PropertyAccessorFactory.forBeanPropertyAccess方法是spring中提供的工具方法,主要用于将指定实例转化为Spring中可以处理的BeanWrapper类型的实例
3、注册Resource的属性编辑器,即在创建Bean的过程中一旦遇到了Resourcel类型的注入就使用ResourceEditor去解析。
4、将之前封装的propertyValues放到生成的BeanWrapper中。
5、接下来就是servletBean的初始化initServletBean()
初始化ServletBean的逻辑在FrameworkServlet中
protected final void initServletBean() throws ServletException { this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'"); if(this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = this.initWebApplicationContext(); this.initFrameworkServlet(); } catch (ServletException var5) { this.logger.error("Context initialization failed", var5); throw var5; } catch (RuntimeException var6) { this.logger.error("Context initialization failed", var6); throw var6; } if(this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization completed in " + elapsedTime + " ms"); } }
1、WebApplicationContext的初始化
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if(this.webApplicationContext != null) { wac = this.webApplicationContext; if(wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac; if(!attrName.isActive()) { if(attrName.getParent() == null) { attrName.setParent(rootContext); } this.configureAndRefreshWebApplicationContext(attrName); } } } if(wac == null) { wac = this.findWebApplicationContext(); } if(wac == null) { wac = this.createWebApplicationContext(rootContext); } if(!this.refreshEventReceived) { this.onRefresh(wac); } if(this.publishContext) { String attrName1 = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName1, wac); if(this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]"); } } return wac; }
首先当前的servlet会从servletContext中的缓存中取到WebApplicationContext,就是我们先前通过监听器添加的WebApplicationContext,对应的Key为WebApplicationContext.class.getName() + ".ROOT"
取到的容器是dispacher-servlet的容器的父容器。
接下来就是寻找及创建当前servlet的WebApplicationContext了
1、通过构造函数的的注入进行初始化
如果当前的servlet已经有了WebApplicationContext的话,说明这是通过构造函数传入的容器,那么接下来就会将之前找到的rootApplicationContext设置为其父容器,并执行configureAndRefreshWebApplicationContext方法
2、通过contextAttribute进行初始化
如果在web.xml中的servlet设置了contextAttribute这个参数,那么这个容器在servletContext中存放的键名就是这个参数的值。因此接下来就会获取这个容器
protected WebApplicationContext findWebApplicationContext() { String attrName = this.getContextAttribute(); if(attrName == null) { return null; } else { WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName); if(wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } else { return wac; } } }
3、如果上述两步还是没有找到对应的容器,那么就要重新实例化一个WebApplicationContext了
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class contextClass = this.getContextClass(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name \'" + this.getServletName() + "\' will try to create custom WebApplicationContext context of class \'" + contextClass.getName() + "\', using parent context [" + parent + "]"); } if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Fatal initialization error in servlet with name \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } else { ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); wac.setParent(parent); wac.setConfigLocation(this.getContextConfigLocation()); this.configureAndRefreshWebApplicationContext(wac); return wac; } }
实例化的逻辑和之前的很相似,这里就不再赘述了。从这边我们可以看到最后也执行了configureAndRefreshWebApplicationContext方法。
configureAndRefreshWebApplicationContext
这个方法用来会已经实例化的WebApplicationContext进行配置及刷新
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if(ObjectUtils.identityToString(wac).equals(wac.getId())) { if(this.contextId != null) { wac.setId(this.contextId); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName()); } } wac.setServletContext(this.getServletContext()); wac.setServletConfig(this.getServletConfig()); wac.setNamespace(this.getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null))); ConfigurableEnvironment env = wac.getEnvironment(); if(env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig()); } this.postProcessWebApplicationContext(wac); this.applyInitializers(wac); wac.refresh(); }
方法里主要对设置了一些属性,例如servletContext,servletConfig等,然后就是一些后处理器的执行:postProcessWebApplicationContext是一个空方法,为了支持拓展,applyInitializers方法则是找到在web.xml配置的初始化参数globalInitializerClasses,并执行其中ApplicationContextInitializer的initialize方法。
接下来就是refresh()方法了
这是一个模板方法,在之前将ApplicationContext容器的时候也说到过,不过之前只是对classPathXmlApplicationContext进行了分析,webApplicationContext的实现逻辑其实也类似
接下来到了刷新onRefresh,这也是一个模板方法
protected void onRefresh(ApplicationContext context) { this.initStrategies(context); }
protected void initStrategies(ApplicationContext context) { this.initMultipartResolver(context); this.initLocaleResolver(context); this.initThemeResolver(context); this.initHandlerMappings(context); this.initHandlerAdapters(context); this.initHandlerExceptionResolvers(context); this.initRequestToViewNameTranslator(context); this.initViewResolvers(context); this.initFlashMapManager(context); }
1、初始化MultipartResolver
MultipartResolver主要用来处理文件上传的,默认情况下Spring是没有multipart处理的。如果要用的话,需要在容器中添加multipart解析器,这样每个请求都会被检查是否包含multipart,并使用容器中定义的resulver来解析它,常用配置如下:
解析器就是在initMultipartResolver方法中,被设置到此servlet中。
2、初始化LocalResolver
支持国际化配置
3、初始化ThemeResolver
支持主题功能
4、初始化HandlerMappings
当客户端发起Request时,DispatcherServlet会将Request提交给HandlerMapping,然后根据配置回传给相应的controller。
默认情况下,spring会加载所有实现handlerMapping接口的Bean,如果只期望Spring加载指定的HandlerMapping,那么可以修改Web.xml中DispatcherServlet的初始化参数,将detectAllHandlerMappings设置为false.
这样Spring会查找名为handlerMapping的bean,若没有找到对应bean,spring会在DispatcherServlet.property中查找org.Springframework.web.servlet.HandlerMapping的内容来加载HandlerMapping
5、初始化HandlerAdapters
支持适配器,将Http请求对象和响应对象传递给Http请求处理器。
6、初始化HandlerExceptionResolvers
提供异常处理
7、初始化RequestToViewNameTranslator
8、初始化ViewResolver
9、初始化FlashMapManager
提供请求储存属性
DispatcherServlet的逻辑处理
在HttpServlet类中分别提供了相应的服务方法,例如doDelete() doGet()、doOptions()、doPost()、doPut()、doTrace(),根据不同的请求方式,servlet会引导至对应的方法中去。对于DispatcherServlet来说,对于不同的请求方式,都是统一交给processRequest()这一个方法来处理的。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Object failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = this.buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null)); this.initContextHolders(request, localeContext, requestAttributes); try { this.doService(request, response); } catch (ServletException var17) { failureCause = var17; throw var17; } catch (IOException var18) { failureCause = var18; throw var18; } catch (Throwable var19) { failureCause = var19; throw new NestedServletException("Request processing failed", var19); } finally { this.resetContextHolders(request, previousLocaleContext, previousAttributes); if(requestAttributes != null) { requestAttributes.requestCompleted(); } if(this.logger.isDebugEnabled()) { if(failureCause != null) { this.logger.debug("Could not complete request", (Throwable)failureCause); } else if(asyncManager.isConcurrentHandlingStarted()) { this.logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); } }
从中我们可以看出具体的逻辑细节转移到了doService方法中了,但在这之前还是做了一些准备和处理工作的、
1、首先提取了当前线程的LocaleContext以及RequestAttribute属性
2、根据当前线程创建对应的localeContext和RequestAttribute,并绑定到当前线程
3、doService做具体逻辑
4、请求处理结束后恢复线程到原始状态
5、请求结束后无论成功与否发布事件通知
接下来看doService的逻辑
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if(this.logger.isDebugEnabled()) { String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":""; this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } HashMap attributesSnapshot1 = null; if(WebUtils.isIncludeRequest(request)) { attributesSnapshot1 = new HashMap(); Enumeration inputFlashMap = request.getAttributeNames(); label108: while(true) { String attrName; do { if(!inputFlashMap.hasMoreElements()) { break label108; } attrName = (String)inputFlashMap.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot1.put(attrName, request.getAttribute(attrName)); } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource()); FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response); if(inputFlashMap1 != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { this.doDispatch(request, response); } finally { if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) { this.restoreAttributesAfterInclude(request, attributesSnapshot1); } } }
实际上这个方法还是在做一些准备工作,包括生成request的属性快照、设置各种resolver等
完整的请求处理过程在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 { try { ModelAndView err = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if(mappedHandler == null || mappedHandler.getHandler() == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter err1 = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if(isGet || "HEAD".equals(method)) { long lastModified = err1.getLastModified(request, mappedHandler.getHandler()); if(this.logger.isDebugEnabled()) { this.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; } err = err1.handle(processedRequest, response, mappedHandler.getHandler()); if(asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, err); mappedHandler.applyPostHandle(processedRequest, response, err); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } this.processDispatchResult(processedRequest, response, mappedHandler, err, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if(asyncManager.isConcurrentHandlingStarted()) { if(mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if(multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
1、首先会先检查是否是文件上传
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if(this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if(WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { this.logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, this typically results from an additional MultipartFilter in web.xml"); } else if(this.hasMultipartException(request)) { this.logger.debug("Multipart resolution failed for current request before - skipping re-resolution for undisturbed error rendering"); } else { try { return this.multipartResolver.resolveMultipart(request); } catch (MultipartException var3) { if(request.getAttribute("javax.servlet.error.exception") == null) { throw var3; } } this.logger.debug("Multipart resolution failed for error dispatch", var3); } } return request; }
如果配置了MultipartResolver,并且当前request是multipartContent类型的话,则转换MultipartHttpServletRequest,并且调用MultipartResolver的resolverMultipart方法对文件做解析。
2、根据request信息寻找对应的Handler
spring默认的handler是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping这两个
如果在配置中配置了<mvc:annotation-driven />的话,会使用RequestMappingHandlerMapping和BeanNameUrlHandlerMapping这两个Handler
我们看一下RequestMappingHandlerMapping的实现逻辑
首先spring会遍历所有的handler的getHandler()方法
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = this.getHandlerInternal(request); if(handler == null) { handler = this.getDefaultHandler(); } if(handler == null) { return null; } else { if(handler instanceof String) { String executionChain = (String)handler; handler = this.getApplicationContext().getBean(executionChain); } HandlerExecutionChain executionChain1 = this.getHandlerExecutionChain(handler, request); if(CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request); CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig; executionChain1 = this.getCorsHandlerExecutionChain(request, executionChain1, config); } return executionChain1; } }
根据request找到对应的handler
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request); if(this.logger.isDebugEnabled()) { this.logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); HandlerMethod var4; try { HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request); if(this.logger.isDebugEnabled()) { if(handlerMethod != null) { this.logger.debug("Returning handler method [" + handlerMethod + "]"); } else { this.logger.debug("Did not find handler method for [" + lookupPath + "]"); } } var4 = handlerMethod != null?handlerMethod.createWithResolvedBean():null; } finally { this.mappingRegistry.releaseReadLock(); } return var4; }
这里首先获取了lookupPath对应的就是请求的uri
接下来根据缓存中解析得到的mapping得到对应的HandlerMethod
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = handler instanceof HandlerExecutionChain?(HandlerExecutionChain)handler:new HandlerExecutionChain(handler); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); Iterator var5 = this.adaptedInterceptors.iterator(); while(var5.hasNext()) { HandlerInterceptor interceptor = (HandlerInterceptor)var5.next(); if(interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor; if(mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
然后将配置中的对应拦截器加入到责任链中。
3、找到对应的HandlerAdapter
找到handlerAdaptor之后会调用其handle方法处理逻辑,对于之前提到的RequestMappingHandlerMapping对应的RequestMappingHandlerAdapter来说,就是执行之前的handlerMethod