在传统的MVC模式中,Tomcat通过读取web.XML配置文件来获取servlet和访问路径的映射关系,这样在访问tomcat就能根据请求路径将请求转发给对应的servlet进行处理。
在我们引入servlet依赖包之后,自定义的servlet是需要继承HttpServlet这个类。servlet的生命周期是要经历的init()、service()、destory()这几个过程,GenericServlet类实现Servlet接口重写三个方法。HttpServlet也继承了GenericeServlet,并且定义了两个抽象的方法doGet()和doPost(),给继承的自定义的servlet去重写。其实HttpServlet重写了init方法将servletResquest和servletResponse强转成HttpServletRequset和HttpServletResponse,在Servlet、GenericeServlet、HttpServlet的关系中是通过模板方法的设计模式来实现根据子类的实现影响父类的行动结果。
下面就是HttpServlet重写service()的部分代码,在父类方法中调用子类实现的doGet方法
接下来转回正题,查看SpringMVC的调用过程,在SpringMVC中只使用了DispatcherServlet来拦截了所有的请求,在上次将filter的调用过程中,最后执行的servlet就是dispatcherServlet,在创建ApplicationFilterChain的时候就将其给赋值进去了。
DispatcherServlet也是继承了HttpServlet类,可以去debug查看DispatcherServlet和HttpServlet之间的继承关系,在接下来的几步父类和子类之间重写方法的互相调用,重点是dispatcherServlet是怎么根据请求路径执行对应的控制器方法的。
主要的执行语句是:AbstractHandlerMapping.class:225
它的作用就是根据request找到保存相对应控制器的方法的元数据信息的对象,即HandlerMethod
通过debug可以看到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping中存储控制器中路径和方法元数据的的数据结构
深入代码看下是如何获取HandleMethod对象的,主要是这个代码
1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { 2 List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList(); 3 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);//根据路径找到对应的RequestMappingInfo对象 4 //mappingRegistry里面保存着控制器所有方法的元数据信息 5 if (directPathMatches != null) { 6 this.addMatchingMappings(directPathMatches, matches, request);//匹配请求方式,将request包含的条件和对应ditrctPathMatches的元素整合创建AbstractHandleMethodMapping对象,添加到matches中 7 } 8 9 //这个是添加全部的了 10 if (matches.isEmpty()) { 11 this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); 12 } 13 14 if (!matches.isEmpty()) { 15 AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);//默认取第一个 16 if (matches.size() > 1) {//有多个通过比较器来进行排序 17 Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request)); 18 matches.sort(comparator); 19 bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0); 20 ...... 21 } 22 23 ......... 24 return bestMatch.handlerMethod; 25 } else { 26 return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); 27 } 28 }
其实到这里我们可以猜出是通过反射调用的方式来请求控制器方法的了,具体的调用语句是:
InvocableHandlerMethod.class:102
剩下的就是获取HandleMethod中的Method和bean、request中的args,就可以进行反射调用了。