3.2.2 登记(缓存):
登记动作发生在registerHandlerMethod 方法中。此时就是把各种关系缓存起来。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); //找:方法搜索器,搜索当前bean继承体系上的所有方法中,符合我们需要的method //.... //注册, for (Map.Entry<Method, T> entry : methods.entrySet()) { //找到可调用的方法 Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); //注册 registerHandlerMethod(handler, invocableMethod, mapping); }} protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); } class MappingRegistry { //三个参数直接信息, handler类,hangler方法 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock();//线程安全 try { //创建一个HandlerMethod HandlerMethod handlerMethod = createHandlerMethod(handler, method); //检查路径是否被定义过,确保一个路径只能找到一个method(路径定义重复的错误就是在这里抛出的) assertUniqueMethodMapping(handlerMethod, mapping); //日志,启动时可以看到 if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } // mappingLookup: 缓存Mapping和handlerMethod的关系 this.mappingLookup.put(mapping, handlerMethod); //获取注解里的路径信息。 List<String> directUrls = getDirectUrls(mapping); //缓存路径与mapping的关系 for (String url : directUrls) { this.urlLookup.add(url, mapping); } // 保存name和handlerMethod的关系 同样也是一对多 String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } //初始化跨域配置,并缓存handlerMethod与跨域配置的 CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } // 注册mapping和MappingRegistration的关系 this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } } } }
总结注册过程做了啥:
- 根据handler,method创建一个HandlerMethod实例
- 缓存mapping和handlerMethod的关系(
mappingLookup
) - 缓存路径url与mapping的关系()
- 缓存name和handlerMethod的关系
- 根据mapping,handlerMethod,urls,name创建一个MappingRegistration相关实例
- 缓存mapping和MappingRegistration的关系
Bean初始化完成后,我们定义的接口方法也就以各种形态,各种关系缓存起来了。
4. 获取handler
当请求来到DispatcherServlet#doDispatch 方法时。遍历所有的handlerMapping, 并尝试从其实例中获取Handler, 如果获取到就返回。
mappedHandler = getHandler(processedRequest); 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; }
对于我们以@RequestMapping("/xxxx")
注解定义的handler。RequestMappingHandlerMapping的父类AbstractHandlerMapping 提供对getHandler的实现。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { 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; } }
上一篇文章说过这里是个模板方法。定义了算法骨架:
- 找handler
- 包装
4.1 匹配
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //获取请求的URI 的path String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { //根据url从多个缓存关系中选择HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); //根据url从关系缓存:urlLookup获取对应的匹配条件集 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } //如果没有找到匹配条件,从整个关系缓存mappingLookup中寻找 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 + "}"); } } //找到匹配后,在Attributes中加入一些相关信息。 request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); //返回最优匹配的handlerMehod return bestMatch.handlerMethod; } else { //没有找到。交给子类去处理没有找到的情况 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
4.2 包装
找到handler后,并不是直接返回handler . 为了 体现框架设计的开闭原则,SpringMVC把拦截与Handler 包装成一个HandlerExecutionChain .
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;
5. 总结
因为篇幅的原因,很多东西看源码理解的更快,本文提供观看思路,加入个人的理解。
框架千千万万,我们不需要把每一个细节看到非常透,抓住主干,理解原理。我们已经成功了一步。