一个人可以走的很快,但一群人可以走的更远 ,新的一年我想遇到优秀的你
上节,说了Handler的4种定义方式,以及Handler与HandlerMapping的关系。
@RequestMapping方式是我们最常用的定义handler的方式。
RequestMappingHandlerMapping 负责把@RequestMapping方式定义handler登记在册。
这一节从源码角度来看看,@RequestMapping注解如何让一个方法变成一个handler。
@RequestMapping注解背后的做的工作其实挺多,但是我们可以抓住几个关键点:
@RequestMapping注解的解析逻辑,是伴随着RequestMappingHandlerMapping 初始化过程完成的
。
一切从RequestMappingHandlerMapping.afterPropertiesSet方法开始。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { 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(); } } 复制代码
主要做了RequestMappingInfo的Builder助手的配置操作,调用父类的afterPropertiesSet()方法
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { public void afterPropertiesSet() { initHandlerMethods(); } //初始化HandlerMethod protected void initHandlerMethods() { //获取所有bean 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) { if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } // 如果是handler, if (beanType != null && isHandler(beanType)) { //识别出Hadnler中的HandlerMethod detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); } } 复制代码
这里的逻辑还是比较清晰的。遍历BeanFactory仓库中Bean,挨个检查类是否是Handler,如果是Handler就去Handler查找HandlerMethod
也就是说: 一个Bean在此会经历两个重点方法,isHandler方法与detectHandlerMethods方法
判断当前Bean是否是一个handler,方法实现在RequestMappingHandlerMapping 类中。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } } 复制代码
类上有@Controller注解或者@RequestMapping 此处就看做是一个Handler
//@RequestMapping("/user") @Controller public class UserController { } 复制代码
虽然@Controller注解的本质也是表示一个@Component
@Component
public @interface Controller {
}
但是,我不用@Component 来表示一个代表Controller类。
其原因就是@Controller 会在RequestMappingHandlerMapping 中按@Controller注解判定为handler。
所以说:@Controller 是表示这是一个跟web有关的Bean
判定为Bean是一个handler 之后,下面就是把类中能处理请求的方法找到,登记在册。也就是 @RequestMapping注解的方法的解析过程
。
两个动作: 找到,登记
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 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); } }} 复制代码
MethodIntrospector
能够搜索到当前类 所有关联的方法(包括,接口,父类,同时还处理参数化方法以及接口和基于类代理的常见场景)中,符合我们要求的方法,返回与之关联的元数据。
MethodIntrospector.MetadataLookup
用于定义条件,匹配当前类所有关联的方法中符合我们要求的方法。
getMappingForMethod就是这个条件。getMappingForMethod是一个抽象方法,在中定义RequestMappingHandlerMapping 中
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { //获取RequestMappingInfo protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { //查找method是是否有RequestMappingInfo RequestMappingInfo info = createRequestMappingInfo(method); //如果方法上有 if (info != null) { //获取类上的RequestMappingInfo RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { //合并方法与类上的注解信息。 这也是为啥。方法的访问路径是类路径+方法路径。 info = typeInfo.combine(info); } } return info; } // 根据方法或者类上的注解信息,创建RequestMappingInfo private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { //获取当前element上的RequestMapping 注解元信息 (包括直接@RequestMapping注解,或者间接RequestMapping 如@GetMapping) // 将查询出的多个annotationType类型注解属性合并到查询的第一个注解中 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); //获取自定义的条件。 RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); //根据注解信息,与自定义条件。创建一个RequestMappingInfo return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } //创建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(); } } 复制代码
简单来说:找的过程,就是 遍历
当前Bean相关的所有方法。 解析
方法上的@RequestMapping注解与类上的@RequestMapping注解, 合并
两者信息,创建出一个 RequestMappingInfo
实例来表示当前方法的@RequestMapping注解元信息
合并这点其实就是解释了 为啥请求处理方法的 Url路径= 类路径+方法路径
最终找的结果:得到一个 以method为K ,以 RequestMappingInfo
为Value的Map methods。也就是解析到了方法与方法对应 RequestMappingInfo
关系映射。
登记动作发生在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(); } } } } 复制代码
总结注册过程做了啥:
mappingLookup
) Bean初始化完成后,我们定义的接口方法也就以各种形态,各种关系缓存起来了。
当请求来到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; } } 复制代码
上一篇文章说过这里是个模板方法。定义了算法骨架:
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(); } } 复制代码
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); } } 复制代码
找到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; 复制代码
因为篇幅的原因,很多东西看源码理解的更快,本文提供观看思路,加入个人的理解。
框架千千万万,我们不需要把每一个细节看到非常透,抓住主干,理解原理。我们已经成功了一步。
如果觉得文章有用, 求点赞:+1: 求关注:heart: 求分享:busts_in_silhouette:
如果本文任何错误,请批评指教,不胜感激 !
微信公众号: [ 源码行动
] , 加我认识认识呗
我把自己写文章的定位是个人理解日记。因为每当我写文章时,我会集中注意力在知识点的理解与阐述上,这样会加深,加强对知识点的理解。