请求映射原理
由于springboot底层使用的是springMVC,所以研究请求映射原理我们需要从DispatcherServlet入手,搜索DispatcherServlet这个包,我们可以发现他是一个继承类,打开继承树我们可以发现上面还有HttpServlet、HttpServletBean、FrameworkServlet这几个父类,我们不妨从最顶层看起。
HttpServletBean继承于HttpServlet,本质也是servlet,所以我们需要研究他的doGet()、doPost()等方法。但是经过搜索发现并没有这些方法,于是我们去子类中寻找。
在FrameworkServlet搜索doGet(),我们可以发现成功找到了这一方法:
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processRequest(request, response);
}
发现他和其他do方法都调用或间接调用了processRequest这一方法,我们再去找这个方法:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
this.initContextHolders(request, localeContext, requestAttributes);
try {
this.doService(request, response);
} catch (IOException | ServletException var16) {
failureCause = var16;
throw var16;
} catch (Throwable var17) {
failureCause = var17;
throw new NestedServletException("Request processing failed", var17);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
this.logResult(request, response, (Throwable)failureCause, asyncManager);
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}
}
前面的初始化过程我们跳过,找到在try下的doService:
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
这是一个抽象方法,说明我们需要去子类中寻找他具体怎么实现的,于是继续查看他的子类:DispatcherServlet
在DispatcherServlet中找到了doService的实现方法(完整代码过长暂且不表)
我们仍然找在try下的doDispatch
try {
this.doDispatch(request, response);
找到这个方法,发现这里是最底层的处理方法,每个请求都会调用这个方法。 最后整理,结构如下图:
为了更好的研究这个方法是怎么工作的,我们打一个断点进行debug测试。在网站成功启动后,我们点击post测试按钮,此时后台变量中request显示是由/user发送过来的请求,我们接着步过进行测试,找到mappedHandler = this.getHandler(processedRequest);
这个函数,我们继续步过这个方法,发现此时mappedHandler这个变量中的handler的值变为了helloController中的post方法调用的函数。所以这个函数的作用是决定哪一个controller方法可以处理当前请求。
在mappedHandler = this.getHandler(processedRequest);
这个函数打上断点并步入,我们发现
他调用了一个handlerMappings的数组,负责处理处理器映射,此时里面有5项
我们打开第二个WelcomePageHandlerMapping
发现pathMatcher里面是根据字符来匹配,在欢迎页这一HandlerMapping中"/" 代表直接访问路径是"/"时,会调用这个处理器,此时的view是index.html,也就是欢迎页。从而实现首页的访问。
RequestMappingHandlerMapping:保存所有@RequestMapping和handler的映射规则。
springMVC启动时对所有Controller和解析的注解都保存到handlerMapping中。然后通过循环将所有的映射进行遍历寻找谁能够处理该请求。
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
接着步过进入第一次循环,也就是遍历RequestMappingHandlerMapping,此时再看mapping这个变量
可以发现,mappingRegistry这个属性里面已经有了所有的类以及能处理的请求。
继续步入步过,直到执行到这个方法
lookuppath就是当前要找的路径,request则是原生的请求。这个方法里有以下语句:
在mappingRegistry这个属性中通过要找的路径进行寻找。从上文我们知道mappingRegistry中有所有的类,根据我们需要的路径进行第一次筛选,继续步过。
我们可以看到,此时directPathMatches里面有4个对象,路径都是/user,只是请求方式不一样。接着运行
if (directPathMatches != null) {
this.addMatchingMappings(directPathMatches, matches, request);
}
其中,addMatchingMappings的作用:
首先对拿到的映射数组遍历,根据顺序一个个匹配,看那个合适,就用那个。
直到找到了可以处理请求的mapping后,就执行add方法,把它加到matcher数组里面
所以我们会得到一个拥有所有匹配结果的集合matches,经过判空等操作后得到的matches如果有多个元素,则会自动选择第一个元素作为最佳选项。元素中如果有多个方法,即对同一个请求有多个方法进行处理时,会报错。因此springboot要求对一个请求只能有一个处理方法。
所有的请求映射都在HandlerMapping中。
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,尝试所有的HandlerMapping看是否有请求信息。如果有就找到这个请求对应的handler,如果没有就是下一个 HandlerMapping。
标签:12,springboot,映射,request,handler,方法,response,请求 From: https://www.cnblogs.com/tarorat/p/17115608.html