页面url访问到后台程序是从一个url开始的,怎么根据url找到对应的处理程序,这个就要从RequestMapping事理理解。
RequestMapping 事理spring-webmvc-4.3.7.RELEASE
SpringMVC 启动的时候,会加载 加了表明 @Controller 的 Bean.
栗子:
@Controller@RequestMapping("/test")public class TestController { @RequestMapping(value = {"/show"}) public String testTest() { return "/jsp/index"; }}
@Controller 表明, 它标记的类便是一个 SpringMVC Controller工具,分发处理器会扫描利用该表明的类的方法,并检测该方法是否利用了@RequestMapping表明。@RequestMapping表明用来把web要求映射到相应的处理函数。
@Controller和@RequestMapping结合起来完成了Spring MVC要求的派发流程。
@RequestMapping 处理流程注册RequestMappingHandlerMapping bean.实例化 RequestMappingHandlerMapping bean.获取 RequestMappingHandlerMaping bean 实例.吸收 Request 要求在 RequestMappingHandlerMapping 实例中查找对应的 Handlerhandler 处理要求。RequestMappingHandlerMapping 是如何实例化的简述:new 出来的,带有父类 AbstractHandlerMethodMapping 属性 mappingRegistry , mappingRegistry key value 中的 value 指的是 带有 requestMapping 表明的方法 。
如果想要 RequestMapping 表明生效,必须在 xml 文件中配置,< mvc:annotation-driven/>。
配置完 xml 之后 ,下一步解析 bean。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //如果该元素属于默认命名空间走此逻辑。Spring的默认namespace为:http://www.springframework.org/schema/beans“ if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; //对document中的每个元素都判断其所属命名空间,然后走相应的解析逻辑 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { //如果该元素属于自定义namespace走此逻辑 ,比如AOP,MVC等。 delegate.parseCustomElement(root); } }
beans 等默认命名空间实行 parseDefaultElement() 方法,其他命名空间实行 parseCustomElement() 方法.
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
进入parseCustomElement(ele, null)方法。
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //获取该元素namespace url String namespaceUri = getNamespaceURI(ele); //得到NamespaceHandlerSupport实现类解析元素 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
进入NamespaceHandlerSupport类的parse()方法。
public BeanDefinition parse(Element element, ParserContext parserContext) { //此处得到AnnotationDrivenBeanDefinitionParser类来解析该元素 return findParserForElement(element, parserContext).parse(element, parserContext); }
获取元素的解析类解析元素
获取解析类
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { String localName = parserContext.getDelegate().getLocalName(element); BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }
解析< mvc:annotation-driven/>元素 进入AnnotationDrivenBeanDefinitionParser类的parse()方法。
表明解析器处理
public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); XmlReaderContext readerContext = parserContext.getReaderContext(); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext); //天生RequestMappingHandlerMapping bean信息 RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); handlerMappingDef.getPropertyValues().add("order", 0); handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); ...... //此处HANDLER_MAPPING_BEAN_NAME值为:RequestMappingHandlerMapping类名 //容器中注册name为RequestMappingHandlerMapping类名 parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)); ...... }
Spring 容器中注册了一个名为 “HANDLER_MAPPING_BEAN_NAME”,类型为 RequestMappingHandlerMapping.
RequestMappingHandlerMapping 实例化
RequestMappingHandlerMapping 继续图
RequestMappingHandlerMapping实现了 HandlerMapping 接口,同时还实现了 ApplicationContectAware 和 IntialingBean 接口。
ApplicationContextAware这个接口只包含以下方法:
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
如果一个类实现了 ApplicationContextAware 接口,Spring容器在初始化该类时候会自动回调该类的setApplicationContext()方法。这个接口紧张用来让实现类得到Spring 容器高下文信息。
initializingBean 接口这个接口包含下面方法:
void afterPropertiesSet() throws Exception;
如果一个bean实现了该接口,Spring 容器初始化bean时会回调afterPropertiesSet()方法。这个接口的紧张浸染是让bean在初始化时可以实现一些自定义的操作。
RequestMappingHandlerMapping 源码剖析RequestMappingHandlerMapping 实现了 ApplicationContextAware 接口 , 那么实例化后会实行 setApplicationContext 方法
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
这个方法把容器高下文赋值给 applicationContext 变量,赋值的便是 spring mvc 容器。
RequestMappingHandlerMapping 实现了 InitializingBean 接口。设置完属性后回调 afterpropertiesSet 方法.
public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); if (getPatternParser() != null) { this.config.setPatternParser(getPatternParser()); Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser."); } else { this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setPathMatcher(getPathMatcher()); } super.afterPropertiesSet(); }
同时还调用了父类的 afterPropertiesSet 方法
public void afterPropertiesSet() { //初始化handler函数 initHandlerMethods(); }
连续查看 RequestMappingHandlerMapping.initHandlerMethods 方法
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } //1.获取容器中所有bean 的name。 //根据detectHandlerMethodsInAncestorContexts bool变量的值判断是否获取父容器中的bean,默认为false。因此这里只获取Spring MVC容器中的bean,不去查找父容器 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); //循环遍历bean 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); } } //2.判断bean是否含有@Controller或者@RequestMappin表明 if (beanType != null && isHandler(beanType)) { //3.对含有表明的bean进行处理,获取handler函数信息。 detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
可以看出 RequestMappingHandlerMapping.initHandlerMethods 方法进行了如下操作:
获取Spring MVC 中的 bean找出 含义 @Controller 和 @RequestMapping 的表明对含义表明 bean 进行解析查看 RequestMappingHandlerMapping.isHandle 处理逻辑
@Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
看对含义 Controller 和 RequestMapping 的怎么处理
查看 RequestMappingHandlerMapping.detectHandlerMethods 方法
protected void detectHandlerMethods(final Object handler) { //1.获取bean的类信息 Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); //2.遍历函数获取有@RequestMapping表明的函数信息 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { try { //如果有@RequestMapping表明,则获取函数映射信息 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } //3.遍历映射函数列表,注册handler for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); //注册handler函数 registerHandlerMethod(handler, invocableMethod, mapping); } };
detectHandlerMethods 紧张功能便是获取该bean和父接口中所有用@RequestMapping表明的函数信息,并把这些保存到methodMap变量中。
查看 RequestMappingHandlerMapping.selectMethods 方法实现逻辑
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) { final Map<Method, T> methodMap = new LinkedHashMap<Method, T>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); Class<?> specificHandlerType = null; //把自身类添加到handlerTypes中 if (!Proxy.isProxyClass(targetType)) { handlerTypes.add(targetType); specificHandlerType = targetType; } //获取该bean所有的接口,并添加到handlerTypes中 handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); /对自己及所有实现接口类进行遍历 for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); //获取函数映射信息 ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { //循环获取类中的每个函数,通过回调处理 @Override public void doWith(Method method) { //对类中的每个函数进行处理 Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); //回调inspect()方法给个函数天生RequestMappingInfo T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { //将天生的RequestMappingInfo保存到methodMap中 methodMap.put(specificMethod, result); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } //返回保存函数映射信息后的methodMap return methodMap; }
上面逻辑中doWith()回调了inspect(),inspect()又回调了getMappingForMethod()方法。
我们看看getMappingForMethod()是如何天生函数信息的。
@Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { //创建函数信息 RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; }
查看RequestMappingHandlerMapping#createRequestMappingInfo()方法。
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { //如果该函数含有@RequestMapping表明,则根据其表明信息天生RequestMapping实例, //如果该函数没有@RequestMapping表明则返回空 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); //如果requestMapping不为空,则天生函数信息MAP后返回 return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
看看RequestMappingHandlerMapping#createRequestMappingInfo是如何实现的。
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, RequestCondition<?> customCondition) { return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()) .customCondition(customCondition) .options(this.config) .build();
可以看到上面把RequestMapping表明中的信息都放到一个RequestMappingInfo实例中后返回。当天生含有@RequestMapping表明的函数映射信息后,末了一步是调用registerHandlerMethod 注册handler和处理函数映射关系。
protectedvoid registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method); }
看到把所有的handler方法都注册到了mappingRegistry这个变量中。
到此就把RequestMappingHandlerMapping bean的实例化流程就剖析完了。
Spring MVC容器初始化流程,查看在 FrameworkServlet#initWebApplicationContext 方法。
protected WebApplicationContext initWebApplicationContext() { //1.得到rootWebApplicationContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; ...... if (wac == null) { // No context instance is defined for this servlet -> create a local one //2.创建 Spring 容器 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. //3.初始化容器 onRefresh(wac); } ...... return wac; }
查看 DispatchServlet#onRefresh 方法
@Override protected void onRefresh(ApplicationContext context) { //实行初始化策略 initStrategies(context); }
查看 initStrategies 方法
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
查看 initHandlerMappings 方法
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. //容器中查找HandlerMapping的实例 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { //把找到的bean放到hanlderMappings中。 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"); } } }
终极是 实例化的 RequestMappingHandlerMapping 放到了一个 HandlerMapping 中。
做事要求是怎么处理的?DispatchServlet 继续自Servlet,那所有的要求都会在service()方法中进行处理。
查看service()方法。
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取要求方法 HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); //若是patch要求实行此逻辑 if (HttpMethod.PATCH == httpMethod || httpMethod == null) { processRequest(request, response); } else { //其它要求走此逻辑 super.service(request, response); } }
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.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); } } } }
得到不管get、post末了都会实行到DispatcherServlet#doDispatch(request, response);
查看 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; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
查看 mappedHandler = getHandler(processedRequest); 方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //获取HandlerMapping实例 for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } //得到处理要求的handler HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
上面遍历handlerMappings得到所有HandlerMapping实例,还记得handlerMappings变量吧,这便是前面initHandlerMappings()方法中设置进去的值,实在便是为了找到 RequestMappingHandlerMapping。
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); ...... HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); ...... return executionChain; }
进入 AbstractHandlerMethodMapping#getHandlerInternal 方法
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //获取函数url String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); ...... try { //查找HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); ...... } finally { this.mappingRegistry.releaseReadLock(); } }
进入 lookupHandlerMethod 方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); ...... }
末了从 mappingRegistry 找到对应的处理方法 这个 mappingRegistry 便是来自 实例化后的 RequestMappingHandlerMapping 。
总结在初始化容器时,会 new 一个RequestMappingHandlerMapping,其父类是AbstractHandlerMethodMapping 。并把这个RequestMappingHandlerMapping 放到一个 HandlerMapping 中。
实例化后带有属性 mappingRegistry , mappingRegistry 的 value 指的是带有 requestMapping 表明的方法。
在做事要求时,遍历 HandlerMapping ,并根据要求信息,从RequestMappingHandlerMapping 中找到对应的处理方法handler
参考资料https://juejin.im/post/6844903829633253389https://www.cnblogs.com/grasp/p/11100124.html欢迎关注"大众号:程序员开拓者社区