看源码不要太专注于记方法的名字
多注意流程
多总结谁要去那干了什么 传入了什么返回了什么
DispatcherServlet
开始说起public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
HandlerMapping
中找到能处理请求的Handler
(Controller.method())。HandlerAdapter
,用的最多的是RequestMappingHandlerAdapter。来分析一下是怎么解析的
这里是从handerMapping里面那个能处理我们的请求
进来看一下
这里面有我们的映射关系
在
里面的
在这里面找那个是我们需要的
从这里面挨个遍历知道找到能处理请求的 之前我们说过 他会先找到第一个 如果后续还能找到能处理这个请求的就会进行各种比较排序判断然后报错 报错的信息大概意思是你有几个方法都能处理这个请求
然后这个请求的返回值如下
找到了能处理我们请求的controller
这个就是我们上面我们写的方法
上面我们已经找到了那个方法能处理我们的请求
这个HandlerAdapter 就是去处理
这些东西的
进来看一下它是怎么确定的
看一下这里面都有什么
0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的
这个方法会返回找到的符合要求的适配器
我们写的controller都默认在在第0个里面 但是如果我们写的是函数式编程的就会在第二个里面
继续往下面看 下面判断是不是GET
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handleInternal(request, response, (HandlerMethod)handler);
}
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
this.checkRequest(request);
ModelAndView mav;
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
} else {
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader("Cache-Control")) {
if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
this.prepareResponse(response);
}
}
return mav;
}
进入到这里
确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers。
看看里面到底是怎么执行的
看看参数解析器
里面有三十个参数解析器
这里面确定我们将要执行的每一个参数的参数值是什么
看看这是什么
这不就是和我们的 @RequestParam 的这个单词一模一样吗
实际上我们用的这个就是由它来解析的
也就是说SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
这是我们的参数解析器的接口
● 当前解析器是否支持解析这种参数
● 支持就调用 resolveArgument
我们下面的这个有多少种返回值取决于上面的这个返回值能设置多少种
我们的返回值能由下面这么多种
有Model,ResponseBody等等
这里我们把找好的处理器 返回值都给放进去
进来看看他要来怎么做
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
this.disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatErrorForReturnValue(returnValue), var6);
}
throw var6;
}
}
第一行执行我们的请求
然后就跳到这了 开始执行我们的方法
看看我们是怎么执行这个方法的
首先获得所有的参数
再看看我们的请求 信息都获取到了
通过反射调用
看看源码
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
...
@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
return doInvoke(args);
}
//本节重点,获取方法的参数值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//查看resolvers是否有支持
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//支持的话就开始解析吧
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
....
}
}
return args;
}
}
打个断点
可以看到获取了参数列表
这里判断参数解析器支不支持参数值
它是怎么判断的?
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
//挨个判断所有参数解析器那个支持解析这个参数
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);//找到了,resolver就缓存起来,方便稍后resolveArgument()方法使用
break;
}
}
}
return result;
}
}
我们来具体分析几个
便利所有的参数解析器 看看谁能支持解析这个参数
遍历是怎么遍历的?
进来看看 先看第一个
下面考研看到它直接判断你有没有 RequestParam这个注解
失败了就return false 然后继续遍历下一个
进入下一个
跟上面的一样 先判断你有没有 RequestParam注解还有是不是Map 的那种 如果不是就继续遍历下一个
进入下一个
看看参数是不是标了 @ParhVariable注解
在看是不是Map类型的
下面的就不在遍历了 原理跟上面的一样
如果找到的话就会放到缓存里面
解析的是怎么解析就不说了 过于复杂了
大概就是根据正则然后匹配之类的
像 WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 这些都是Servlet API
Servlet API 没有注解那么 springboot是怎么获取解析器的?
其实这些解析器也在我们之前看过的那三十个解析器里面
我们针对
看看它的解析器是怎么处理的
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
HandlerMapping要去找三个东西
通过这找到的三个东西解析返回
如何去找?
遍历
这么多的注解
springboot接收到一个请求的时候他会去找这个那个controller能处理这个请求 就遍历它放请求的那个地方 要是找到了多个就会报错
springboot在加载这些注解的时候他会知道这些注解所在的那个大的类里面有什么注解
然后它想要知道这些注解具体是那个的时候他会挨个匹配加载的所有的注解 要是匹配到了就放到缓存里面 下一次找就会快很多
这都找到了就开始调用这些注解代表的方法去解析 解析的方法就是用正则表达式去匹配 然后获取到value