SpringMVC:(二)根据请求查找对应的Controller方法的流程
前言:
在这之前我们已经建立请求和Controller方法的映射集合,接下来我们就要去取出映射关系里获取请求的逻辑实例。
根据请求查找对应的Controller方法的流程主要发生在DispatcherServlet类的doDispatch()方法。该方法的调用时机是当我们访问路径:http://localhost:8080/springmvcdemo/headline/open 的时候
观察下面的流程图我们可以看到
(1)会去访问到DispatcherServlet的父类FrameworkServlet的service()方法
(2)然后会根据请求选择doGet()或者doPost()方法
(3)然后父类会去调用到子类DispatcherServlet的doService()方法去处理逻辑
(4)DispatcherServlet的doService()方法会去调用doDispatch()去派遣给Controller执行 => (根据请求查找对应的Controller方法)就是在该方法中调用了getHandler()去获取的
这里我们重新回到RequestMappingHandlerMapping类中。
我们观察其类图:
通过观察类图我们发现该类还实现了HandlerMapping接口,该接口就一个getHanndler()方法。该方法只需要给其传递HttpServletRequest参数即可返回HandlerExecutionChain对象
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
类图上显示HandlerMapping接口的主要实现类为:AbstractHandlerMapping类。
接下来我们跟进到AbstractHandlerMapping类的getHanndler()方法。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //获取handler的具体逻辑,留给子类实现 Object handler = getHandlerInternal(request); if (handler == null) { //如果获取到的handler为null则采用默认的handler,即属性defaultHandler handler = getDefaultHandler(); } if (handler == null) { //如果连DefaultHandler也为空,则直接返回空 return null; } // Bean name or resolved handler? //如果handler是beanName,从容器里获取对应的bean if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } //根据handler和request获取处理器链HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//获取到执行器链
//下面的代码与主要流程无关(略) if (logger.isTraceEnabled()) {logger.trace("Mapped to " + handler);} else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {logger.debug("Mapped to " + executionChain.getHandler());} if (hasCorsConfigurationSource(handler)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;//返回执行器链 }
(1)调用子类的getHandlerInternal()方法去获取handler的具体逻辑(AbstractHandlerMethodMapping.getHandlerInternal()方法)
(A)获取到Request(请求)中的URL(用来匹配handler)
(B)调用lookupHandlerMethod()方法,根据路径寻找Handler,并封装成HandlerMethod
(a)尝试从先前映射建立的时候存入的urlLookup中查找映射
(b)如果匹配到了,检查其他属性是否符合要求,如请求方法,参数,hander等 (没有直接匹配到,则遍历所有的处理方法进行通配符匹配)
(c)如果方法有多个匹配,不同的通配符等,则排序选择出最合适的一个
(d)如果有多个匹配的,会找到第二个最合适的进行比较。
(e)给请求(request)设置上属性参数
(f)返回匹配的URL的处理的方法
(C)根据handlerMethod中的bean来实例化Handler,并添加进HandlerMethod
(2)如果获取到的handler为null则采用默认的handler,即属性defaultHandler
(3)如果连DefaultHandler也为空,则直接返回空
(4)如果上面获取到的处理方法(handler)是实现了String接口,说明其只是一个beanName,此时我们要去容器中获取到真正对应的Bean
(5)根据处理方法(handler)和请求(request)获取处理器链(HandlerExecutionChain)
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { //判断handler是不是执行器链,如果不是则创建一个执行器链 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH); //包装拦截器 /*可以去做一些请求的时候业余上对用户信息的拦截 <mvc:interceptor> <mvc:mapping path="/shopadmin/**" /> <bean id="ShopInterceptor" class="com.imooc.o2o.interceptor.shopadmin.ShopLoginInterceptor" /> </mvc:interceptor> */ for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain;//将包装好的执行器链返回
}
(A)创建获取到执行器链(判断传入的handler(处理方法)是不是执行器链,如果不是则创建一个执行器链,如果是则强转为执行器链)
(B)对获取到的执行器链进行包装拦截器(将配置好的拦截器for循环遍历然后筛选path,如果符合当前这个执行器链,则对其进行包装)
(6)无关逻辑(略)
(7)返回处理器链