经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
java?spring?mvc处理器映射器介绍
来源:jb51  时间:2022/3/29 16:18:08  对本文有异议

前言:

  • 本文源码基于spring-framework-5.3.10
  • mvcspring源码中的一个子模块!

一、RequestMappingHandlerMapping解析映射简单介绍

  • @RequestMapping通过RequestMappingHandlerMapping进行解析!
  • HandlerMapping是一个根据URL映射到Handler的方法。
  • RequestMappingHandlerMapping是HandlerMapping的一个子类!
  • RequestMappingHandlerMapping他的父类有InitializingBean,所有在spring启动实例化的时候会调用afterPropertiesSet()方法。解析逻辑就在这里。
  • RequestMappingHandlerMapping有俩个过程:解析、映射

二、@RequestMapping解析源码流程

  • 容器加载,调用RequestMappingHandlerMappingafterPropertiesSet()。
  • 调用父类的afterPropertiesSet()方法。
  • 调用initHandlerMethods()方法。
  • 循环每一个Bean,看方法上有@RequestMapping或者@Controller的Bean。
  • 解析HandlerMethods,进行封装RequestMappingInfo。
  • 将封装好的RequestMappingInfo存起来:key为路径,值为mapping(RequestMappingInfo)

三、@RequestMapping映射源码流程

  • 请求进来,调用getHandler方法。
  • 获取当前请求对应的HandlerMethod
  • 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径。
  • 更具路径去pathLookup中找。
  • 上面没找到,从所有的里面找有通配符的。
  • 找到多个进行排序,优先级:? > * > {} >** 。
  • 不为空拿到第一个返回。
  • 如果为空获取默认的。默认还是空的,直接返回null。
  • 封装拦截器,返回。

四、@RequestMapping解析源码

  1. /**
  2. * 解析的开始位置。
  3. * 由于实现了InitializingBean,初始化Bean的时候调用这个方法。
  4. * 源码位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet()
  5. */
  6. public void afterPropertiesSet() {
  7.  
  8. this.config = new RequestMappingInfo.BuilderConfiguration();
  9. this.config.setTrailingSlashMatch(useTrailingSlashMatch()); // 尾部斜杠
  10. this.config.setContentNegotiationManager(getContentNegotiationManager());
  11.  
  12. if (getPatternParser() != null) {
  13. this.config.setPatternParser(getPatternParser());
  14. Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
  15. "Suffix pattern matching not supported with PathPatternParser.");
  16. }
  17. else {
  18. this.config.setSuffixPatternMatch(useSuffixPatternMatch());
  19. this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
  20. this.config.setPathMatcher(getPathMatcher());
  21. }
  22. // 调用父类的afterPropertiesSet方法
  23. super.afterPropertiesSet();
  24. }
  25.  
  26. /**
  27. * 父类的afterPropertiesSet方法。
  28. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet()
  29. */
  30. public void afterPropertiesSet() {
  31. initHandlerMethods();
  32. }
  33.  
  34. /**
  35. * 解析@RequestMapping方法
  36. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods()
  37. */
  38. protected void initHandlerMethods() {
  39. // 获得所有候选beanName—— 当前容器所有的beanName
  40. for (String beanName : getCandidateBeanNames()) {
  41. // BeanName不是scopedTarget.开头的
  42. if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
  43. // *处理候选bean——即解析@RequestMapping和映射路径
  44. processCandidateBean(beanName);
  45. }
  46. }
  47. // 解析完所有@RequestMapping的时候调用
  48. handlerMethodsInitialized(getHandlerMethods());
  49. }
  50.  
  51. /**
  52. * 处理候选bean——即解析@RequestMapping和映射路径
  53. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(String)
  54. */
  55. protected void processCandidateBean(String beanName) {
  56. Class<?> beanType = null;
  57. try {
  58. // 得到当前BeanName得到这个Bean的类型
  59. beanType = obtainApplicationContext().getType(beanName);
  60. }
  61. catch (Throwable ex) {
  62. // An unresolvable bean type, probably from a lazy bean - let's ignore it.
  63. if (logger.isTraceEnabled()) {
  64. logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
  65. }
  66. }
  67. // 这一步判断是关键:是否有Controller 或 RequestMapping注解
  68. if (beanType != null && isHandler(beanType)) {
  69. // 解析HandlerMethods
  70. detectHandlerMethods(beanName);
  71. }
  72. }
  73.  
  74. /**
  75. * 解析HandlerMethods
  76. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(Object)
  77. */
  78. protected void detectHandlerMethods(Object handler) {
  79. Class<?> handlerType = (handler instanceof String ?
  80. obtainApplicationContext().getType((String) handler) : handler.getClass());
  81.  
  82. if (handlerType != null) {
  83. Class<?> userType = ClassUtils.getUserClass(handlerType);
  84. // 循环所有方法
  85. Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
  86. (MethodIntrospector.MetadataLookup<T>) method -> {
  87. try {
  88. // 根据Method得到Mapping映射
  89. return getMappingForMethod(method, userType);
  90. }
  91. catch (Throwable ex) {
  92. throw new IllegalStateException("Invalid mapping on handler class [" +
  93. userType.getName() + "]: " + method, ex);
  94. }
  95. });
  96. if (logger.isTraceEnabled()) {
  97. logger.trace(formatMappings(userType, methods));
  98. }
  99. else if (mappingsLogger.isDebugEnabled()) {
  100. mappingsLogger.debug(formatMappings(userType, methods));
  101. }
  102. // 遍历每一个方法,这里是所有Bean的所有方法
  103. methods.forEach((method, mapping) -> {
  104. Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
  105. // pathLookup放入:key为路径,值为mapping(RequestMappingInfo)
  106. registerHandlerMethod(handler, invocableMethod, mapping);
  107. });
  108. }
  109. }
  110.  
  111. /**
  112. * 根据Method得到Mapping映射
  113. * 源码位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(Method, Class<?>)
  114. */
  115. protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  116. // 如果方法上面有@RequestMapping:解析出RequestMappingInfo
  117. // RequestMappingInfo 是用来在请求的时候做匹对的
  118. RequestMappingInfo info = createRequestMappingInfo(method);
  119. if (info != null) {
  120. // 如果方法上面有@RequestMapping,看看类上面是不是有@RequestMapping
  121. RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
  122. // 类上面也有@RequestMapping 那就合并
  123. // 比如 类:/user 方法:/info 合并为 /user/info
  124. if (typeInfo != null) {
  125. info = typeInfo.combine(info);
  126. }
  127.  
  128. // 合并前缀 5.1新增 默认null
  129. // 可通过 WebMvcConfigurer#configurePathMatch 进行定制
  130. String prefix = getPathPrefix(handlerType);
  131. if (prefix != null) {
  132. info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
  133. }
  134. }
  135. return info;
  136. }
  137.  
  138. /**
  139. * 创建请求映射信息的外部逻辑
  140. * 源码位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(AnnotatedElement)
  141. */
  142. private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
  143. // 获取RequestMapping注解信息
  144. RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
  145. // 获取请求调解:[可扩展], 如果有:该条件会在请求时匹对
  146. RequestCondition<?> condition = (element instanceof Class ?
  147. getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
  148. // 如果有RequestMapping注解,封装成RequestMappingInfo
  149. return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
  150. }
  151.  
  152. /**
  153. * 创建请求映射信息的内部逻辑
  154. * 源码位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMapping, RequestCondition<?>)
  155. */
  156. protected RequestMappingInfo createRequestMappingInfo(
  157. RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
  158. // 将@RequestMapping注解属性的值构建成一个 RequestMappingInfo
  159. RequestMappingInfo.Builder builder = RequestMappingInfo
  160. //构建路径
  161. .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
  162. //构建方法(get还是post等)
  163. .methods(requestMapping.method())
  164. //参数 对应http request parameter
  165. .params(requestMapping.params())
  166. //头部
  167. .headers(requestMapping.headers())
  168. //request的提交内容类型content type,如application/json, text/html
  169. .consumes(requestMapping.consumes())
  170. //指定返回的内容类型的content type,仅当request请求头中的(Accept)类型中包含该指定类型才返回
  171. .produces(requestMapping.produces())
  172. .mappingName(requestMapping.name());
  173. if (customCondition != null) {
  174. builder.customCondition(customCondition);
  175. }
  176. // 构造RequestMappingInfo:将上面的属性构建成一个个的RequestCondition对象方便在请求的时候组合匹对
  177. return builder.options(this.config).build();
  178. }
  179.  
  180. /**
  181. * 得到所有的方法
  182. * 源码位置:org.springframework.core.MethodIntrospector.selectMethods(Class<?>, MetadataLookup<T>)
  183. */
  184. public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
  185. final Map<Method, T> methodMap = new LinkedHashMap<>();
  186. Set<Class<?>> handlerTypes = new LinkedHashSet<>();
  187. Class<?> specificHandlerType = null;
  188. //获取原始的class对象
  189. if (!Proxy.isProxyClass(targetType)) {
  190. specificHandlerType = ClassUtils.getUserClass(targetType);
  191. handlerTypes.add(specificHandlerType);
  192. }
  193. //获取class的接口
  194. handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
  195. //循环我们的class集合
  196. for (Class<?> currentHandlerType : handlerTypes) {
  197. final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
  198.  
  199. ReflectionUtils.doWithMethods(currentHandlerType, method -> {
  200. //获取具体的方法对象
  201. Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
  202. /**回调 即解析@RequestMapping 返回RequestMappingInfo
  203. * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod(java.lang.reflect.Method, java.lang.Class)*/
  204. T result = metadataLookup.inspect(specificMethod);
  205. if (result != null) {
  206. // 看看有没有桥接方法:泛型实现类jvm会自动生成桥接类
  207. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
  208. if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
  209. //把方法对象作为key,RequestMappingInfo对象作为value保存到map中
  210. methodMap.put(specificMethod, result);
  211. }
  212. }
  213. }, ReflectionUtils.USER_DECLARED_METHODS);
  214. }
  215.  
  216. return methodMap;
  217. }

五、@RequestMapping映射源码

  1. /**
  2. * 获取@RequestMapping映射
  3. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(HttpServletRequest)
  4. */
  5. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  6. // 获取当前请求对应的HandlerMethod
  7. Object handler = getHandlerInternal(request);
  8. // 获取默认的handler
  9. if (handler == null) {
  10. handler = getDefaultHandler();
  11. }
  12. // 还是没有handler的时候返回null,404了
  13. if (handler == null) {
  14. return null;
  15. }
  16. // Bean name or resolved handler?
  17. // String类型?获取Bean
  18. if (handler instanceof String) {
  19. String handlerName = (String) handler;
  20. handler = obtainApplicationContext().getBean(handlerName);
  21. }
  22.  
  23. // Ensure presence of cached lookupPath for interceptors and others
  24. if (!ServletRequestPathUtils.hasCachedPath(request)) {
  25. initLookupPath(request);
  26. }
  27.  
  28. // 获取拦截器相关的调用链
  29. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  30.  
  31. if (logger.isTraceEnabled()) {
  32. logger.trace("Mapped to " + handler);
  33. }
  34. else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
  35. logger.debug("Mapped to " + executionChain.getHandler());
  36. }
  37.  
  38. if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
  39. CorsConfiguration config = getCorsConfiguration(handler, request);
  40. if (getCorsConfigurationSource() != null) {
  41. CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
  42. config = (globalConfig != null ? globalConfig.combine(config) : config);
  43. }
  44. if (config != null) {
  45. config.validateAllowCredentials();
  46. }
  47. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  48. }
  49.  
  50. return executionChain;
  51. }
  52.  
  53. /**
  54. * 获取当前请求对应的HandlerMethod
  55. * 源码位置:org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(HttpServletRequest)
  56. */
  57. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  58. request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
  59. try {
  60. // 直接调用父类的getHandlerInternal方法
  61. return super.getHandlerInternal(request);
  62. }
  63. finally {
  64. ProducesRequestCondition.clearMediaTypesAttribute(request);
  65. }
  66. }
  67. /**
  68. * 获取当前请求对应的HandlerMethod---父类的
  69. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(HttpServletRequest)
  70. */
  71. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  72. // 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径
  73. String lookupPath = initLookupPath(request);
  74. this.mappingRegistry.acquireReadLock();
  75. try {
  76. // 通过lookupPath解析最终的handler——HandlerMethod对象
  77. HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
  78. return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
  79. }
  80. finally {
  81. this.mappingRegistry.releaseReadLock();
  82. }
  83. }
  84.  
  85. /**
  86. * 通过lookupPath解析最终的handler
  87. * 源码位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(String, HttpServletRequest)
  88. */
  89. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  90. List<Match> matches = new ArrayList<>();
  91. // 根据uri从mappingRegistry.pathLookup获取 RequestMappingInfo
  92. // pathLookup<path,RequestMappingInfo>会在初始化阶段解析好
  93. List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
  94. if (directPathMatches != null) {
  95. // 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
  96. addMatchingMappings(directPathMatches, matches, request);
  97. }
  98. if (matches.isEmpty()) {
  99. // 如果无path匹配,用所有的RequestMappingInfo 通过AntPathMatcher匹配
  100. addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
  101. }
  102. if (!matches.isEmpty()) {
  103. // 选择第一个为最匹配的
  104. Match bestMatch = matches.get(0);
  105. /**
  106. * 如果匹配到多个
  107. @RequestMapping(value="/mappin?")
  108. @RequestMapping(value="/mappin*")
  109. @RequestMapping(value="/{xxxx}")
  110. @RequestMapping(value="/**")
  111. */
  112. if (matches.size() > 1) {
  113. //创建MatchComparator的匹配器对象
  114. Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
  115.  
  116. /** 根据精准度排序 大概是这样的: ? > * > {} >** 具体可以去看:
  117. * @see org.springframework.util.AntPathMatcher.AntPatternComparator#compare(java.lang.String, java.lang.String)*/
  118. matches.sort(comparator);
  119.  
  120. // 排完序后拿到优先级最高的
  121. bestMatch = matches.get(0);
  122. if (logger.isTraceEnabled()) {
  123. logger.trace(matches.size() + " matching mappings: " + matches);
  124. }
  125. // 是否配置CORS并且匹配
  126. if (CorsUtils.isPreFlightRequest(request)) {
  127. for (Match match : matches) {
  128. if (match.hasCorsConfig()) {
  129. return PREFLIGHT_AMBIGUOUS_MATCH;
  130. }
  131. }
  132. }
  133. else {
  134. //获取第二最匹配的
  135. Match secondBestMatch = matches.get(1);
  136. //若第一个和第二个是一样的 抛出异常
  137. if (comparator.compare(bestMatch, secondBestMatch) == 0) {
  138. Method m1 = bestMatch.getHandlerMethod().getMethod();
  139. Method m2 = secondBestMatch.getHandlerMethod().getMethod();
  140. String uri = request.getRequestURI();
  141. throw new IllegalStateException(
  142. "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
  143. }
  144. }
  145. }
  146. //把最匹配的设置到request中
  147. request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
  148. handleMatch(bestMatch.mapping, lookupPath, request);
  149. //返回最匹配的
  150. return bestMatch.getHandlerMethod();
  151. }
  152. else { // return null
  153. return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
  154. }
  155. }

到此这篇关于java spring mvc处理器映射器介绍的文章就介绍到这了,更多相关spring mvc处理器映射器内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号