HandlerMapping
介绍
HandlerMapping
处理器映射器,是SpringMvc
的核心组件之一,用来根据请求的request
信息查询对应的Handler
,在web
环境中,每个请求都需要一个对应的Handler
来处理,所以当接收到一个请求,需要哪一个Handler
来处理,HandlerMapping
的作用就是找到处理的那个Handler
。
分析
1. HandlerMapping顶层接口
以上为HandlerMapping
的类图,在HandlerMapping
接口中有一个公共的抽象类AbstractHandlerMapping
所有的子孙都会继承它。该抽象类有两个子类AbstractHandlerMethodMapping
表示基于方法的映射方法,这个方式就是我们日常使用的Controller
的那种方式,AbstractUrlHandlerMapping
表示根据url
获取到对应的Handler
。
这里分析一下公共抽象父类AbstractHandlerMapping。这个抽象类采用了模板方法的设计模式,编写了HandlerMapping
的核心逻辑getHandler()
方法,获取具体的handler
由子类继承并实现getHandlerInternal
方法,在获取到具体的handler
之后,添加该请求匹配的拦截器列表,再返回HandlerExecutionChain
结构,里面包含了具体的handler
和拦截器列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); }
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()); }
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
|
这里点一下初始化拦截器的方法。由于AbstractHandlerMapping
间接继承于ApplicationContextAware
接口,在bean
初始化时会调用该接口进行applicationContext
的赋值。而改方法中设置了一个模板方法接口,由具体子类实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { 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 { if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
protected void initApplicationContext(ApplicationContext context) throws BeansException { initApplicationContext(); }
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
|
根据以下代码可以得出一个简单的结论,拦截器的作用范围实际上是在Handler
执行的前后,而过滤器Filter
作用的范围应该是在请求进入到servlet
的前和执行完servlet
逻辑之后,套在springmvc
中,Filter
的作用范围是在请求进入到DispatcherServlet
之前和执行完DispatcherServlet
之后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
|
2. AbstractHandlerMethodMapping
AbstractHandlerMethodMapping
这个就是RequestMappingHandlerMapping
的抽象顶层父类,这种映射方式就是我们日常开发的那种,将标记有@Controller
的类中的每个标记有@RequestMapping
的方法都抽象为一个对应的HandlerMethod
。
简单罗列一下使用到的类:
- HandlerMethod:在初始化
RequestMappingHandlerMapping
会将spring
容器中标记有@Controller
的类中的@RequestMapping
的方法都封装为HandlerMethod
实体,包含了Handler
对应的方法以及ControllerBean
,并提供一些访问参数、方法返回值、获取注解等方法。
- RequestMappingInfo:在
@Controller
类上标记的@RequestMaping
或者是在方法上标记的@RequestMapping
最终都会加载到RequestMappingInfo
实体中。
- MappingRegistration:主要记录
RequestMappingInfo
与HandlerMethod
关系
- MappingRegistry:一个注册表,它维护到处理程序方法的所有映射,公开执行查找的方法并提供并发访问。
AbstractHandlerMethodMapping分支的类图:
图中红框中的三个类,分别依次继承,我们日常开发所使用的就是RequestMappingHandlerMapping
。
2.1 初始化
在springbean
容器启动后,当初始化类RequestMappingHandlerMapping
完成时,由于它间接实现了初始化的后置方法InitializingBean
,所以会进入afterPropertiesSet
方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet(); }
@Override public void afterPropertiesSet() { initHandlerMethods(); }
protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); }catch (Throwable ex) { if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } }
|
在接下来就是将具体的@Controller
标记的类中的方法进行解析,满足条件的method
封装为HandlerMethod
后,添加到注册表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); }catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) { final Map<Method, T> methodMap = new LinkedHashMap<>(); Set<Class<?>> handlerTypes = new LinkedHashSet<>(); Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(targetType)) { specificHandlerType = ClassUtils.getUserClass(targetType); handlerTypes.add(specificHandlerType); } handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, 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; }
|
这里使用了两次的函数式接口,不同函数式接口可能看到这回有点吃力。第一个函数式接口的作用其实就是将某个method
上标记的@RequestMapping
信息和这个method
所在类上标记的@RequestMapping
信息拼接起来后包装为RequestMappingInfo
后返回。第二个函数式接口的作用是将第一个函数式接口处理之后返回的RequestMappingInfo
信息暂存到某个变量中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
public void register(T mapping, Object handler, Method method) { if (KotlinDetector.isKotlinType(method.getDeclaringClass())) { Class<?>[] parameterTypes = method.getParameterTypes(); if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) { throw new IllegalStateException("Unsupported suspending handler method detected: " + method); } } this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); validateMethodMapping(handlerMethod, mapping); 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<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
|
2.2 查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); }
if (!matches.isEmpty()) { Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } 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(); 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); } }
|