前言
本文我们先来解析一下AbstractHandlerMethodMapping,以对下篇文章EndpointHandlerMapping的解析打下基础.其继承结构如下:
解析
HandlerMapping
HandlerMapping的顶层接口,只声明了1个方法–>getHandler–>调用getHandler实际上返回的是一个HandlerExecutionChain,这是典型的command的模式的使用,这个HandlerExecutionChain不但持有Handler本身,还包括了处理这个HTTP请求相关的拦截器,如下:
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
AbstractHandlerMapping
HandlerMapping的抽象实现,模板方法模式,将1些共性的方法抽象成1个类.其实现了getHandler.如下:
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.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
根据request获取对应的handler,该方法是1个抽象方法,由子类来实现,如下:
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
- 如果没有对应的handler,就是要默认的handler
- 如果没有默认的handler,返回null
- 通过名称取出对应的 handler bean
把handler 封装到HandlerExecutionChain中并加上拦截器.如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // 1. 获得HandlerExecutionChain HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // 2. 根据请求获得对应的Path String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // 3. 遍历adaptedInterceptors for (HandlerInterceptor interceptor : this.adaptedInterceptors) { // 3.1 如果是MappedInterceptor,并且匹配当前的path,则加入到HandlerExecutionChain中 if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } // 3.2 否则,直接加入到HandlerExecutionChain else { chain.addInterceptor(interceptor); } } return chain; }
- 获得HandlerExecutionChain
- 根据请求获得对应的Path
遍历adaptedInterceptors
- 如果是MappedInterceptor,并且匹配当前的path,则加入到HandlerExecutionChain中
- 否则,直接加入到HandlerExecutionChain
跨域处理
- 获得全局的CorsConfiguration和handler对应的CorsConfiguration,然后进行合并
- 针对cors请求添加Handler或者Interceptor
AbstractHandlerMethodMapping
AbstractHandlerMethodMapping是1个泛型类,其泛型参数T–>用来代表匹配handler的条件专门使用的一种类,这里的条件就不只是url了,还可以有很多其他条件,如request的类型,请求的参数,header等都可以作为匹配的HandlerMethod的条件.默认使用的是RequestMappingInfo实现了InitializingBean接口.
字段如下:
// scpoed 代理 bean的name的前缀.用来去除handler method的判断 private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget."; // cors请求并且是options类型的请求并且请求头中含有Access-Control-Request-Method时返回的HandlerMethod private static final HandlerMethod PREFLIGHT_AMBIGUOUS_MATCH = new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle")); private static final CorsConfiguration ALLOW_CORS_CONFIG = new CorsConfiguration(); static { ALLOW_CORS_CONFIG.addAllowedOrigin("*"); ALLOW_CORS_CONFIG.addAllowedMethod("*"); ALLOW_CORS_CONFIG.addAllowedHeader("*"); ALLOW_CORS_CONFIG.setAllowCredentials(true); } // 如果为true,则在当前applicationContext和祖先applicationContext中获取所有的bean,如果为false,则在当前上下文获得所有的bean private boolean detectHandlerMethodsInAncestorContexts = false; // 向MappingRegistry中的nameLookup进行注册时用来生成beanName,这里默认使用的是RequestMappingInfoHandlerMethodMappingNamingStrategy // 其规则为:类名里的大写字母组合+"#"+方法名. private HandlerMethodMappingNamingStrategy<T> namingStrategy; private final MappingRegistry mappingRegistry = new MappingRegistry();
这里有必要说明1下MappingRegistry,其字段如下:
private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>(); // 保存着匹配条件(也就是RequestMappingInfo)和HandlerMethod的对应关系 private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>(); // 保存着url与匹配条件(也就是RequestMappingInfo)的对应关系,当然这里的url是pattren式的,可以使用通配符. // 由于RequestMappingInfo可以同时使用多种不同的匹配方式而不只是url一种,所有反过来说同一个url就可能有多个RequestMappingInfo与之对应 // 这里的RequestMappingInfo其实就是在@RequestMapping 中注释的内容 private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>(); // 这个map是spring mvc 4 新增的,保存着name与HandlerMethod的对应关系,这个name是从HandlerMethodMappingNamingStrategy的实现类从 // HandlerMethod中解析处理的,默认使用的是RequestMappingInfoHandlerMethodMappingNamingStrategy,解析规则是: // 类名里的大写字母组合+"#"+方法名.这个在正常的匹配过程不需要使用,它主要用在MvcUriComponentsBuilder里,可以根据name获取相应的url private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<String, List<HandlerMethod>>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<HandlerMethod, CorsConfiguration>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
由于AbstractHandlerMethodMapping实现了InitializingBean,因此在其初始化之后,会调用其afterPropertiesSet方法,如下:
public void afterPropertiesSet() { 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()); }
- 获得ApplicationContext中的所有bean的id
遍历
- 如果bean Id 不是 scopedTarget.开头的,则获得其类型
如果其是handler则调用detectHandlerMethods进行注册.其中hanler是抽象方法. detectHandlerMethods如下:
protected void detectHandlerMethods(final Object handler) { // 获得handler的类型 Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); // 如果是cgli代理的子对象类型,则返回父类型,否则直接返回传入的类型 final Class<?> userType = ClassUtils.getUserClass(handlerType); // 获取当前bean里所有符合Handler要求的Method 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); } } }); // 将符合要求的methods注册起来,也就是保存到3个map中 for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
- 获得handler的类型,如果是cgli代理的子对象类型,则返回父类型,否则直接返回传入的类型
- 获取当前bean里所有符合Handler要求的Method,其中会回调getMappingForMethod方法,该方法是个抽象方法,由子类实现
将符合要求的methods注册.代码如下:
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
MappingRegistry#register 实现如下:
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); // 检查是否在mappingLookup已经存在,如果存在而且和现在传入的不同则抛出异常 assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } // 添加到mappingLookup中 this.mappingLookup.put(mapping, handlerMethod); // 添加到urlLookup List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } // 添加到nameLookup String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } // 实例化CorsConfiguration 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(); } }
创建HandlerMethod,代码如下:
protected HandlerMethod createHandlerMethod(Object handler, Method method) { HandlerMethod handlerMethod; if (handler instanceof String) { String beanName = (String) handler; handlerMethod = new HandlerMethod(beanName, getApplicationContext().getAutowireCapableBeanFactory(), method); } else { handlerMethod = new HandlerMethod(handler, method); } return handlerMethod; }
检查是否在mappingLookup已经存在,如果存在而且和现在传入的不同则抛出异常.代码如下:
private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) { HandlerMethod handlerMethod = this.mappingLookup.get(mapping); if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException( "Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" + handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped."); } }
- 添加到mappingLookup中
添加到urlLookup,其中getDirectUrls–>获得mapping的Path,如果不含有*或者含有?的话,则添加到结果集中.代码如下:
private List<String> getDirectUrls(T mapping) { List<String> urls = new ArrayList<String>(1); for (String path : getMappingPathPatterns(mapping)) { if (!getPathMatcher().isPattern(path)) { urls.add(path); } } return urls; }
AntPathMatcher#isPattern,如下:
public boolean isPattern(String path) { return (path.indexOf('*') != -1 || path.indexOf('?') != -1); }
- 添加到nameLookup
- 实例化CorsConfiguration,如果不为null,则添加到corsLookup.此处默认返回null,由子类复写
- 添加到registry中
模板方法,空实现
getHandlerInternal实现如下(删去多余代码):
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 1.截取用于匹配的url有效路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 2. 使用lookupHandlerMethod方法通过lookupPath和request 找HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); // 3. 如果可以找到handlerMethod则调用createWithResolvedBean方法创建新的HandlerMethod return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); }
- 截取用于匹配的url有效路径
使用lookupHandlerMethod方法通过lookupPath和request 找HandlerMethod.代码如下:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { // match 是内部类,保存匹配条件和HandlerMethod List<Match> matches = new ArrayList<Match>(); // 1. 根据lookupPath获取到匹配条件 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 将匹配到的条件添加到matches addMatchingMappings(directPathMatches, matches, request); } // 如果不能直接使用lookupPath得到匹配条件,则将所有匹配条件加入到matches if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } // 对matches进行排序,并取第一个作为bestMatch,如果前面两个排序相同则抛出异常 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; } // 如果matches有多个匹配的,则将第2个和第一个进行比较,看顺序是否一样,如果是的话,则抛出异常 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 + "}"); } } // 在返回前做一些处理,默认实现是讲lookupPath设置到request的属性,子类RequestMappingInfoHandlerMapping // 进行了重写,将更多的参数设置到了request,主要是为了以后使用时方便 handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
- 根据lookupPath获取到匹配条件,将匹配到的条件添加到matches
- 如果不能直接使用lookupPath得到匹配条件,则将所有匹配条件加入到matches
如果matches非空
- 对matches进行排序,并取第一个作为bestMatch,如果前面两个排序相同则抛出异常
- 在返回前做一些处理,默认实现是讲lookupPath设置到request的属性,子类RequestMappingInfoHandlerMapping进行了重写,将更多的参数设置到了request,主要是为了以后使用时方便
否则,调用handleNoMatch,默认返回null.
如果可以找到handlerMethod则调用createWithResolvedBean方法创建新的HandlerMethod.代码如下:
public HandlerMethod createWithResolvedBean() { Object handler = this.bean; if (this.bean instanceof String) { String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); }
RequestMappingInfoHandlerMapping
RequestMappingInfoHandlerMapping–> 继承自AbstractHandlerMethodMapping
字段,构造器如下:
// 对OPTIONS请求的处理时用到 private static final Method HTTP_OPTIONS_HANDLE_METHOD; static { try { HTTP_OPTIONS_HANDLE_METHOD = HttpOptionsHandler.class.getMethod("handle"); } catch (NoSuchMethodException ex) { // Should never happen throw new IllegalStateException("Failed to retrieve internal handler method for HTTP OPTIONS", ex); } } protected RequestMappingInfoHandlerMapping() { setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy()); }
该类复写了几个方法:
getMappingPathPatterns,代码如下:
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) { return info.getPatternsCondition().getPatterns(); }
该方法是在hander注册的时候调用.如下:
getMatchingMapping–>检查给定的RequestMappingInfo是否匹配当前的请求,返回RequestMappingInfo,代码如下:
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { return info.getMatchingCondition(request); }
getMappingComparator–> 返回1个比较RequestMappingInfo的Comparator,在有多个Handler匹配当前请求时用到.代码如下:
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) { return new Comparator<RequestMappingInfo>() { @Override public int compare(RequestMappingInfo info1, RequestMappingInfo info2) { return info1.compareTo(info2, request); } }; }
handleMatch,handleNoMatch比较简单,这里就不再贴出
RequestMappingHandlerMapping
RequestMappingHandlerMapping–> 继承自RequestMappingInfoHandlerMapping.根据 在实现Controller接口或者被@Controller注解的类中的在类和方法上声明的@RequestMapping,创建一个 RequestMappingInfo
字段如下:
// 是否使用后缀匹配(.*)当对请求进行模式匹配时,如果可用时,则/users 对/users.*也匹配.默认是true. private boolean useSuffixPatternMatch = true; // 是否后缀匹配应该只对ContentNegotiationManager中注册的扩展符匹配时生效.这一般建议减少歧义和避免问题比如当.出现在路径的情况下 private boolean useRegisteredSuffixPatternMatch = false; // 是否有无斜杠都匹配,如果启用的化,则/users 也匹配 /users/.默认是true private boolean useTrailingSlashMatch = true; // 内容协商 private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); // 这里使用的是EmbeddedValueResolver private StringValueResolver embeddedValueResolver; // RequestMappingInfo的Builder类,用来创建RequestMappingInfo的 private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
其主要方法如下:
覆写了afterPropertiesSet,如下:
public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager()); super.afterPropertiesSet(); }
isHandler,代码如下:
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
调用链如下:
getMappingForMethod –> 使用在类,方法 声明的@RequestMapping来创建RequestMappingInfo.代码如下:
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { // 1. 根据Method上的@RequestMapping 创建RequestMappingInfo RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { // 2. 根据类上的@RequestMapping 创建RequestMappingInfo RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { // 3. 合并 info = typeInfo.combine(info); } } return info; }
createRequestMappingInfo,如下:
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { // 获取@RequestMapping 注解 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); // 此处返回的都是null RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
- 获取@RequestMapping 注解
- 获得RequestCondition,此处返回的都是null
如果requestMapping等于null,则返回null,否则根据RequestMapping创建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(); }