朋友们,我胡汉三又回来了,本期想跟大家一起聊聊Spring MVC处理请求的那些事。
面试的时候,我都喜欢问候选人常见的Spring MVC注解,大家都会说,RequestMapping,ResponseBody,RestController吧啦吧啦,听到说RequestMapping,我就会往下问,RequestMapping注解中有哪些方法,这个时候,就能看到大家平时只是使用,还是有去了解过注解中的方法,不过也不怪,平时用的时候,写个value,甚至默认不写任何方法都可以。本文笔者预备从RequestMapping出发,分析请求的处理过程。
相信大家在系统启动过程中,都见过类似如下的信息,不知道有多少人去翻看代码,查看这个日志从哪打印,而且看起来,这些日志是将请求的url与请求处理的类和方法关联起来,想到这一层,那就往前迈了一大步了。
RequestMappingHandlerMapping实现了InitializingBean接口,该接口提供了afterPropertiesSet方法,在Spring容器初始化过程中,当RequestMappingHandlerMapping所有的属性都被注入之后会去调用这个afterPropertiesSet()方法,如下,
跟进去,找到initHandlerMethods方法,
其中有一个isHandler方法,划重点,看该方法,
会发现,处理的是带@Controller或@RequestMapping注解的类。总算是守得云开见月明了,写到此处,我就想起面试问侯选人,请求的处理过程怎样,有些了解过的人,会说通过DispatcherServlet做分发,通过handlerMapping找到请求处理的方法,再找到handlerAdapter处理完返回ModelAndView,带我再细问,handlerMapping有哪些实现类,往往有些人就会很茫然。
再往下,找到最终处理的地方,
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
看到了,这就是控制台输出的日志。
最终@RequestMapping注解的信息被封装在RequestMappingInfo中,在mappingLookup中与HandlerMethod一一对应,urlLookup中建立请求的url与RequestMappingInfo的关系。至此,分析了RequestMapping的url与其对应的处理类和处理方法之间如何建立关联,以及前面看到的控制台输出信息。
到此,你以为本文就要结束了吗,并没有,接下来,笔者要测试几个问题了,
-
相同的类,如果我在不同方法上,加上所有属性都相同的@ReqeustMapping
-
相同的类,如果我在不同方法上,加上value相同的@ReqeustMapping,但其他属性不同(如method)
-
不同的类,加上所有属性都相同的@ReqeustMapping
那么结果会如何,列位看官可以结合上面的介绍,先自己想一下,接下来为大家揭晓
我造了如下的符合第一种情况的请求,
启动系统,得到如下报错信息,
那么该错从哪报出来的,为什么又会报这个错呢?
我们注意到,register方法中,有一个
this.assertUniqueMethodMapping(handlerMethod, mapping);
方法的具体代码如下:
可以看到,该方法通过mappingLookup缓存中获得是否有当前RequestMappingInfo对应的HandlerMethod,即是否有使用相同的@RequestMapping注解,如果有,重点看equals方法的判断,
可以看到,比较是否属于同一个Bean,方法对应的类及方法名称是否一致,并且方法的参数类型和参数个数是否一致,任意一个条件不满足,则认为该RequestMapping映射异常。
由上面的分析,那么对于场景2,Spring是可以正常启动的。
对于场景3,不同的类中,HandlerMethod对应的Bean不同,因此,也一样会报上面启动的错。
总结
平时工作中,深究系统启动过程中的各种信息,并了解工作中常见的注解及其底层的原理,以上,与君共勉~