SpringMVC在@RequestMapping配置两个相同路径

  这篇博客来自这个问题: 在SpringMVC中@RequestMapping可以配置两个相同的url路径吗。

  首先,这个问题会点SpringMVC的人可能都知道答案,但是上次面试中我就回答了可以。。。可以。。Spicy Chicken!!!

  参考文章: http://lgbolgger.iteye.com/blog/2105108

  

  这个问题要从 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 讲起了。 

  首先,在配置文件中声明了 <mvc:annotation-driven /> 注解之后, 在 initStrategies() 方法中注册了处理的类:

 1 protected void initStrategies(ApplicationContext context) {
 2         initMultipartResolver(context);
 3         initLocaleResolver(context);
 4         initThemeResolver(context);
 5      initHandlerMappings(context);
 6         initHandlerAdapters(context);
 7         initHandlerExceptionResolvers(context);
 8         initRequestToViewNameTranslator(context);
 9         initViewResolvers(context);
10         initFlashMapManager(context);
11     }  

  容器对 @ReqeustMapping 便签的处理的简化流程就是首先 RequestMappingHandlerMapping 类去查找有 @Controller 或 @RequestMapping 的类, 然后为含有其中一个注解的类还有类中含有 @RequestMapping 注解的方法构建 HandlerMethod 对象, 之后 RequestMappingHandlerAdapter 判断是否 support 对应的方法并执行对应的方法。

  具体的过程如下:

  1, RequestMappingHandlerMapping 遍历所有的 bean, 判断是否有 @Controller 或 @RequestMapping 注解

 1     protected void initHandlerMethods() {
 2         if (logger.isDebugEnabled()) {
 3             logger.debug("Looking for request mappings in application context: " + getApplicationContext());
 4         }
 5
 6         String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
 7                 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
 8                 getApplicationContext().getBeanNamesForType(Object.class));
 9
10         for (String beanName : beanNames) {
11             if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
12           isHandler(getApplicationContext().getType(beanName))){
13                 detectHandlerMethods(beanName);
14             }
15         }
16         handlerMethodsInitialized(getHandlerMethods());
17     }  

  isHandler() 判断的方法:

1     protected boolean isHandler(Class<?> beanType) {
2         return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
3                 (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
4     }  

  注意方法中的判断是用 || 逻辑,说明两者当中的其中一个符合即可。

  2,处理含有 @RequestMapping 的方法,遍历第一步中找到的类中的所有方法,用一个 MethodFilter 查找所有的 @ReqeustMapping 注解的方法,并为找到的 @RequestMapping 的方法构建 ReqeustMappingInfo 对象

 1 /**
 2      * Look for handler methods in a handler.
 3      * @param handler the bean name of a handler or a handler instance
 4      */
 5     protected void detectHandlerMethods(final Object handler) {
 6         Class<?> handlerType =
 7                 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
 8
 9         // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
10         final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
11         final Class<?> userType = ClassUtils.getUserClass(handlerType);
12
13         Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
14             @Override
15             public boolean matches(Method method) {
16                 T mapping = getMappingForMethod(method, userType);
17                 if (mapping != null) {
18                     mappings.put(method, mapping);
19                     return true;
20                 }
21                 else {
22                     return false;
23                 }
24             }
25         });
26
27         for (Method method : methods) {
28             registerHandlerMethod(handler, method, mappings.get(method));
29         }
30     }  

  其中查找 @RequestMapping 注解的方法的 getMappingForMethod() 方法:

 1 /**
 2      * Uses method and type-level @{@link RequestMapping} annotations to create
 3      * the RequestMappingInfo.
 4      * @return the created RequestMappingInfo, or {@code null} if the method
 5      * does not have a {@code @RequestMapping} annotation.
 6      * @see #getCustomMethodCondition(Method)
 7      * @see #getCustomTypeCondition(Class)
 8      */
 9     @Override
10     protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
11         RequestMappingInfo info = null;
12         RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
13         if (methodAnnotation != null) {
14             RequestCondition<?> methodCondition = getCustomMethodCondition(method);
15             info = createRequestMappingInfo(methodAnnotation, methodCondition);
16             RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
17             if (typeAnnotation != null) {
18                 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
19                 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
20             }
21         }
22         return info;
23     }  

  构建 RequestMappingInfo 对象的 createRequestMappingInfo() 方法:

 1 /**
 2      * Created a RequestMappingInfo from a RequestMapping annotation.
 3      */
 4     protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
 5         String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
 6         return new RequestMappingInfo(
 7                 new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
 8                         this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
 9                 new RequestMethodsRequestCondition(annotation.method()),
10                 new ParamsRequestCondition(annotation.params()),
11                 new HeadersRequestCondition(annotation.headers()),
12                 new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
13                 new ProducesRequestCondition(annotation.produces(), annotation.headers(), this.contentNegotiationManager),
14                 customCondition);
15     }  

  3,构建完 RequestMappingInfo 对象后, 存储到 Map 类型的 handlerMethods 对象中

 1 /**
 2      * Register a handler method and its unique mapping.
 3      * @param handler the bean name of the handler or the handler instance
 4      * @param method the method to register
 5      * @param mapping the mapping conditions associated with the handler method
 6      * @throws IllegalStateException if another method was already registered
 7      * under the same mapping
 8      */
 9     protected void registerHandlerMethod(Object handler, Method method, T mapping) {
10         HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
11         HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
12         if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
13             throw new IllegalStateException("Ambiguous mapping found. Cannot map ‘" + newHandlerMethod.getBean() +
14                     "‘ bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already ‘" +
15                     oldHandlerMethod.getBean() + "‘ bean method\n" + oldHandlerMethod + " mapped.");
16         }
17
18         this.handlerMethods.put(mapping, newHandlerMethod);
19         if (logger.isInfoEnabled()) {
20             logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
21         }
22
23         Set<String> patterns = getMappingPathPatterns(mapping);
24         for (String pattern : patterns) {
25             if (!getPathMatcher().isPattern(pattern)) {
26                 this.urlMap.add(pattern, mapping);
27             }
28         }
29     }  

   ***在这个地方, 给出了文章开头的问题的答案, 如果存在 @RequestMapping 注解有相同的访问路径时,会在此处抛出异常 (注释很重要)*************

  4, 构建完了  private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap(); 这个对象之后,该对象就包含了所有的它应该包含的 bean 了,它的key为RequestMappingInfo对象,value为handler和它中含有@RequestMapping注释的方法method构建的HandlerMethod。

  5, url 匹配路径时, RequestMappingHandlerAdapter 依据为是否是HandlerMethod 类型判断是否执行该方法。(这点我不太懂)

1 public final boolean supports(Object handler) {
2         return handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler);
3     }  


  最后,针对文章开头的问题, 对第三步步骤截图做一个简单的示例。

  声明两个相同路径的 @RequestMapping 注解并查看错误:

  

  查看错误:

  

  正好是步骤三中抛出异常的位置,问题结束!!



  一直用 IntelliJ 查看 Spring 的源码, 今天用 Maven 工程,查看源码的时候直接可以打开源码的文件,不用再 attach Source 了,而且发现 eclipse 里面的源码还带有英文的注释, IntelliJ 反编译后的源码文件都不带注释的, 以后的源码查看可能要改改了, 算是新发现吧。这个问题就到这了( ̄▽ ̄)~*

时间: 2024-10-25 02:50:20

SpringMVC在@RequestMapping配置两个相同路径的相关文章

springmvc中针对一个controller方法配置两个url请求

springmvc中针对一个controller方法配置两个url请求 标签: spring mvc孙琛斌 2015-12-10 17:10 2189人阅读 评论(0) 收藏 举报  分类: Spring(8)  版权声明:本文为博主原创文章,未经博主允许不得转载. 记录一个小知识点. 某些应用场景>..你可能需要不同的url请求得到相同的结果,那么你写两个方法总是不太好的,使用下面的方法可以解决这个问题. @RequestMapping(value = { "/item/index.ht

【SpringMVC】---RequestMapping、Ant 路径、PathVariable 注解、HiddenHttpMethodFilter 过滤器、用 POJO 作为参数

一.web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/

浅析 SpringMVC 原理和配置.

一.原理 Spring MVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序,将请求处理的逻辑和视图中的渲染实现解耦. 1.DispatcherServlet是Spring MVC的核心 .Spring MVC 中的请求页面都会委托给DispatcherServlet来执行处理. 2.DispatcherServlet需要知道将请求发送给哪个控制器,所以DispatcherServlet会查询

springmvc基于java配置的实现

该案例的github地址:https://github.com/zhouyanger/demo/tree/master/springmvc-noxml-demo 1.介绍 之前搭建SpringMvc项目要配置一系列的配置文件,比如web.xml,applicationContext.xml,dispatcher.xml.Spring 3.X之后推出了基于JavaConfig方式以及注解的形式的配置.在一定程度上简化了Spring项目的配置.近几年特别火的SpringBoot,大大的简化了创建项目

SpringMVC框架入门配置 IDEA下搭建Maven项目

,初衷:本人初学SpringMVC的时候遇到各种稀奇古怪的问题,网上各种技术论坛上的帖子又参差不齐,难以一步到位达到配置好的效果,这里我将我配置的总结写到这里供大家初学SpringMVC的同僚们共同学习使用! 关于SpringMVC的介绍我就不在此赘述了,想必每一个配置Spring的人都已经或多或少了解过一些SpringMVC的基础知识,可能有的人用的myeclipse或者eclipse进行项目的搭建,我会告诉你这里步骤是很相似的,在myeclipse里面需要建立web项目,然后加入Spring

SpringMVC+springSecurity+flexPaper 配置--类似百度文库在线预览

背景:现在项目需要做一个类似百度文库的在线预览功能,在网上找了下很多人推荐使用FlexPaper,所以今天尝试学习了FlexPaper顺便集成到现有的框架中 由于网上目前的说的都不是很详细,所以现在记录在此,希望对需要的人有所帮助 准备:1. FlexPaper_2.2.4.zip 下载地址:http://flexpaper.devaldi.com/download/             2.swftools 下载地址:http://www.swftools.org/download.htm

springmvc注解@RequestMapping

springmvc注解@RequestMapping 1.处理器.controller的url 2)跟路径+子路径. 3)限定提交方法 @RequestMapping的属性method: 1.RequestMehtod.Get 2.RequestMethod.POST 常用的两个. 原文地址:https://www.cnblogs.com/meiLinYa/p/8761328.html

SpringMVC(三) RequestMapping修饰类

SpringMVC使用@RequestMapping 注解为控制器指定可以处理哪些URL请求. 可以用于类定义以及方法定义: 类定义:提供初步的请求映射信息.相对于WEB应用的根目录. 方法处:提供进一步的细分映射信息.相对于类定义处的URL.若类定义处没有定义,则是相对于根目录. 如:针对类设置了@RequestMapping("pathclass")注解,针对方法设置了@RequestMapping("method"),则最终调用到方法的url为pathclas

CentOS 6.5 x64 安装Tomcat8 并配置两个Tomcat8

1.首先,安装tomcat的前提是已经配置好jdk环境变量,若没配好可以参考我的上一篇博文:CentOS 6.5 x64安装jdk8,当然也可以通过网络搜索安装步骤~~ 2.下载: 可以通过官网下载:http://tomcat.apache.org/download-80.cgi,我下载的是这个. 3.在xftp中,进入/usr/local目录,新建一个文件夹为tomcat8,进入后建立两个文件夹(因为我们要装两个tomcat),分别为tom8081(代表8081端口),tom8082(代表80