我们浏览器发送请求的时候,我们的后台是怎么知道这个请求要去寻找COntoller中的哪个方法呢?
我们已经知道SpringBoot 是一个整合框架的框架,在Web部分,SPringBoot 底层使用的是SpringMvc, SpringMvc是通过DispatcherServlet 来执行请求分发的
打开类结构树
原生的Servlet 被HttpServletBean 继承, HttpServletBean又被FramworkServlet继承,最后被我们的DispatcherServlet继承
Servlet 用来接收请求必定会有用到doGet 和 doPost 方法
HttpServletBean
这里面并没有比较核心的doget 或者是dopost方法,再看 FramworkServlet
发现这个类重写了doget dopost 方法 而且这些方法最终都将request 和responce请求进行了包装 processRequest()方法
进入processRequest()方法查看
进入DoService方法(),结果发现这是一个抽象方法,在这个类中并没有定义具体的实现,那么他的实现自然就交给了他的子类, DispatcherServlet 进入DispathcerServlet的类中 查看doservice方法
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration<?> attrNames = request.getAttributeNames();
label116:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label116;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
this.doDispatch(request, response); // 核心方法, 上面的代码都是在做初始化,为这一条做准备
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null){
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
try {
this.doDispatch(request, response); // 核心方法, 上面的代码都是在做初始化,为这一条做准备 每个请求都会经过这个方法
}
我们进入这个方法 看看他到底做了什么!
进入dodispath()方法,打上断点访问 localhost:8080
这里发现原生的请求是 / 也就是访问的是我们的主页
继续向下放行有一个mappedHandler 表示这个请求被哪个映射处理器接收了
从标注的第一行开始, 第一行的意思是 检查这个请求是不是文件上传请求,检查结果是这个不是文件啥贯穿请求, 然后 MapperHandller 的值为 ParameterizableViewController 接收了,并且通过试图解析器,重定向到了 Index.html , 这解释了 为什么Spring 只要静态资源有INdex.html 访问主页就会定向到index.html 页面了
我们现在断点打在
在访问/get 请求 localhost:8080/get 这个请求映射写在TestController2 的get 方法上面
发现他直接就找到了 这个TestController 2 的 get 方法, 那么他是怎么找到的呢 这时候我就需要把注意力放在断点语句的右边 this.getHandler(ProcessedRequest)
进入这个方法
发现他 发现了5个handlerMapping 分别如下
这段代码的意思 , 请求进来,会依次遍历这五个请求映射器, 如果有哪一个请求映射器可以匹配到这个请求,那么就把这个映射器给返回出去, 从图上蓝色代码可以看到, springboot.controller.testcontroller2 .get 匹配到了这个请求, 就把这个映射给返回出去了
我们在Mapping.getHandler(request) 打上断点 重新请求/get 看看他是怎么找到这个映射器的, 进入了这个类
也就是说 getHandlerInternal(request) 找到了这个映射, 我们再断点调试 在Object Handler 打上断点请求/get
发现在这个地方就找到了这个映射,也就是handlerMethod,createWithResolvedBean 找到了 我们继续断点请求调试
emm 分析不动了, 不过 分析出来了 他通过哪些方法 一步一步的找到了这个类,. ,不过重新分析回来查看这个断点, 我们会发现,我们在controller中写的所有请求都会在mappingRegistry 中被找到, 应该是通过,反射和注解 来做到的这些我也不太清楚了,以我目前的能力我只能分析到这里了
我知道为什么了, 我们请求/get 这个请求, 这个请求在 五个handlmapping 中的 requesthandllermapping 中的 mappingregistry 中找到了这个请求, 所以这个请求就被 requesthandlermapping 给接受返回了
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
this.addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
this.addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (matches.isEmpty()) {
return this.handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
} else {
AbstractHandlerMethodMapping<T>.Match bestMatch = (Match)matches.get(0);
if (matches.size() > 1) {
Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new MatchComparator(this.getMappingComparator(request));
matches.sort(comparator);
bestMatch = (Match)matches.get(0);
if (this.logger.isTraceEnabled()) {
Log var10000 = this.logger;
int var10001 = matches.size();
var10000.trace("" + var10001 + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
Iterator var7 = matches.iterator();
while(var7.hasNext()) {
AbstractHandlerMethodMapping<T>.Match match = (Match)var7.next();
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
} else {
AbstractHandlerMethodMapping<T>.Match secondBestMatch = (Match)matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
this.handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
}
先记录着,下次再总结 一下