前言
前面我們簡單地分析了兩個過程,本文將會來介紹請求過來是如何找到對應的Controller中的方法的。
概述
在分析之前,我們先記住幾個類名,HandlerMapping接口,RequestMapingHandlerMapping類,HandlerMethod類。
- HandlerMapping接口:請求和處理方法的映射接口,提供了getHandler方法,我們可以通過這個接口的實現類來完成請求和處理方法的映射
- RequestMappingHandlerMapping:請求和處理方法映射關系的實現類,這是方法是我們分析的重點
- HandlerMethod:處理方法,這個方法封裝了許多東西,比如我們加了@controller注解的對象、對應Controller的方法、方法的參數、方法的注解、方法參數的注解等等。
關注一下,RequestMappingHandlerMapping的繼承關系,幾點說明一下
- 實現了HandlerMapping接口
- 實現了InitialzingBean接口,這個接口我們知道,在IOC完成注入后,會有一個初始化處理過程,這個過程中會調用這個接口實現類的afterPropertiesSet方法,對於RequestMappingHandlerMapping來說,主要是在這個方法里完成了映射關系的注冊
- 實現了ApplicationAware接口,會在這個實現方法時進行攔截的器的查找的注冊
這里是HttpMethod的屬性,本篇我們關注下bean和method兩個屬性,其它屬性后面會談到。
web.xml配置和之前的一樣,這里不貼圖了,看一下spring-servlet-config.xml的配置
源碼分析
看上圖,context命名空間下的,我們不進行分析,關注<mvc:annotation-driven/>。分析事務、AOP時,我們知道了除了spring的基礎命名空間外,其它標簽的解析都是走parseCustomElement方法的。而對標簽是解析是交給特點的解析器的。我們先在spring-webmvc的META-INF/spring.handlers方法找到MvcNamespaceHander,再在MvcNamespaceHandler找到AnnotationDrivenBeanDefinitionParser解析器。我們先看這個解析器的parse方法。
- AnnotationDrivenBeanDefinitionParser解析<mvc:annotation-driven/>標簽。這個方法太長,這里只貼出這里分析要用到的核心代碼。我們閱讀下面的關鍵代碼,發現解析標簽時注冊了RequestMappingHandlerMapping和BeanNameUrlHandlerMapping兩個處理器,后面這個處理器,有點類似struts2。這兩個處理器的order值前者小於后者,並且開發時前者對所有的Controller都支持,所以在平常的開發中,走的都是前者的邏輯。所以,我們這里只對RequestMappingHandlerMapping進行分析。
//AnnotationDrivenBeanDefinitionParser類的方法
//解析<mvc:annotation-driven/>,spring mvc的核心注解,有了它可以給我們注入映射器、適配器、異常處理器等等,這里我們關注映射器,而不用我們一個個<bean>標簽地去配置了
public BeanDefinition parse(Element element, ParserContext parserContext) { ......
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
......
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
......
}
//MvcNamespaceUtils類的方法
//注冊默認組件,這里我們關注下BeanNameUrlHandlerMapping的注冊
public static void registerDefaultComponents(ParserContext parserContext, Object source) { registerBeanNameUrlHandlerMapping(parserContext, source); registerHttpRequestHandlerAdapter(parserContext, source); registerSimpleControllerHandlerAdapter(parserContext, source); }
- 通過上過的解析過程,我們已經把RequestMappingHandlerMapping給注冊到容器了。接下來就是實例化了,通過概述中的整個繼承體系中,我們知道RequestMappingHandlerMapping是實現了InitialzingBean接口的,而實現接口的類是AbstractHandlerMethodMapping。而這個類的afterPropertiesSet方法是整個映射關系建立的入口,我們從這里開始分析。這里我們可以看到有以下幾步
- 遍歷所有的bean
- 如果是scope proxy則不管(這個在AOP中再補充進去),否則判斷是不是handler。比較簡單,就是看看類是否有@Controller或@RequestMapping注解
- 如果這個類是handler,則會去查找這個類的所有方法
- 最后的一個handlerMethodsInitialized方法是個空方法,不用管了
//AbstractHandlerMethodMapping類的方法
//映射關系建立的入口
public void afterPropertiesSet() { initHandlerMethods(); }
//AbstactHandlerMethodMapping類的方法
//建立映射關系
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()); }
//RequestMappingHandlerMapping類的方法
//判斷是否為handler
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
- 查找Controller下所有帶有@RequestMapping注解的方法,最后注冊HandlerMethod
//AbstractHandlerMethodMapping類的方法
//查找並注冊帶HandlerMethod
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { try { 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); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
- 查找所有帶@RequestMapping的方法,並存放到Map<Method,RequestMappingInfo>中,這里我們注意下RequestMappingInfo中的patternsCondition屬性就是我們解析注解中的url
//MethodIntrospector類的方法
//遍歷所有的方法,包括父類中的,匹配到的則注冊到map中
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; if (!Proxy.isProxyClass(targetType)) { handlerTypes.add(targetType); specificHandlerType = targetType; } 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); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } return methodMap; }
//RequestMappingHandlerMapping類的方法
//判斷是否有@RequestMapping注解
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
//RequestMappingHandlerMapping類的方法
//這里就是把@RequestMappingHandlerMapping中的信息封裝到RequestMappingInfo對象中
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(); }
public RequestMappingInfo build() { ContentNegotiationManager manager = this.options.getContentNegotiationManager(); PatternsRequestCondition patternsCondition = new PatternsRequestCondition( this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(), this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(), this.options.getFileExtensions()); return new RequestMappingInfo(this.mappingName, patternsCondition, new RequestMethodsRequestCondition(methods), new ParamsRequestCondition(this.params), new HeadersRequestCondition(this.headers), new ConsumesRequestCondition(this.consumes, this.headers), new ProducesRequestCondition(this.produces, this.headers, manager), this.customCondition); }
- 注冊HandlerMethod,首先我們看到注冊信息是存放在AbstractHandlerMethodMapping的mappingRegistry屬性,而這個屬性對應的類是AbstractHandlerMethodMapping的一個內部類,下面我們看在這個類上的注冊過程。
- 創建HandlerMethod,這個比較簡單,就是把bean名字、對應是IOC容器、方法等這些屬性設置到HandlerMethod中
- 建立mapping與handlerMethod的映射
- 查找@RequestMapping注解中的url地址(這個在上一步中已經保存在RequestMappingInfo對象中),建立url和mapping的映射關系
- 建立mapping和MappingRegistration(這里名包含了mapping、handlerMethod、url等所有的信息)
到這里所有的映射關系都建立好的,通過url就可以找到maping信息,通過mapping信息就對找到handlerMethod、mappingRegistration等所有所有信息了。接下來,我們就來看調用過程。
//AbstractHandlerMethodMapping類的方法
//注冊處理器方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
//AbstractHandlerMethodMapping的內部類
//映射關系都是注冊在這個類上的
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
- 調用過程,讓我們來到調用過程。之前我們分析過,調用過程入口doDispatch方法,這里只貼出我們關心的一點代碼,即拿到請求對應的handler。我們看到返回的是HandlerExecutionChan對象,這個類有handler和攔截器的屬性,本篇我們的關注點放到handler屬性上,攔截器后面用單獨的章節來討論。
//DispatcherServlet類方法
//請求調用過程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... HandlerExecutionChain mappedHandler = null; ...... mappedHandler = getHandler(processedRequest); ....... }
//HandlerExecutionChain的屬性
private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList;
- 遍歷所有的HandlerMapping,在上面的分析中,我們說過這里會有兩個HandlerMapping對象:RequestMappingHandlerMapping和BeanNameUrlHandlerMapping對象。因為RequestMappingHandlerMapping的優先級高,會先去其中查找handler。通過后面的分析,我們會知道在RequestMappingHandlerMapping中會找到相應的handler並返回到DispatcherServlet。
//DispatcherServlet類的方法
//獲取處理器鏈
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; }
//RequestMappingHandlerMapping的父類AbstractHandlerMapping的方法
//查找handler和攔截器,形成處理鏈
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; }
- 現在的過程是比較清楚的,分為下面幾步
- 從請求中解析出url
- 根據url從之前說過的url與handlerMethod中找到拿到的handlerMethod集合
- 選出了適合的handlerMethod返回回去,所以HandlerExecutionChain中的handler屬性是一個handlerMethod對象
//AbstractHandlerMethodMapping類的方法
//根據請求獲取合適的HandlerMethod對象
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
//AbstractHandlerMethodMapping類的方法
//根據url獲取合適的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); } }
public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
總結
整個流程還是比較清楚的,解析標簽<mvc:annotation-driven/>、實例化RequestMappingHandlerMapping時會建立映射關系、請求時查找映射關系。接下來會接着對攔截器進行分析
參考鏈接
- https://www.cnblogs.com/weknow619/p/6341395.html(ContextLoaderListener與DispatcherServlet的創建的上下文)
- http://www.cnblogs.com/fangjian0423/p/springMVC-dispatcherServlet.html(源碼分析參考博文)
- https://blog.csdn.net/qq_21033663/article/details/52374436(解析器參考博文)