spring mvc的代码相对比较简单,因为是线性的,并且是单线程的。。。
使用的spring boot debug运行查看源码的。
从DispatcherServlet的doDispatch开始分析,前面的逻辑无关紧要。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 这个类里,保存了拦截器,handler(封装了将要调用的方法信息,包括controller,method,parameter等)
HandlerExecutionChain mappedHandler = null;
// DispatchServlet中初始化了很多HandlerMapping,HandleMapping对应请求的处理方式,这里,找寻一个能够处理这个请求的handleMapping,封装成HandlerExecutionChain
// 一般我们用的是requestHandlermapping,初始化的时候,会添加所有拦截器
// 没有能够处理这个请求的HandleMapping,则报错
// 通过handlerMapping构造HandlerExecutionChain的时候,会通过url,找到适配的拦截器,就是在这个方法里
mappedHandler = getHandler(processedRequest);
// DispatcchServlet中初始化了很多HandleAdapter,找寻一个支持这个HandleExecutionChain的
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 这个方法,会遍历调用所有的拦截器的preHandle方法
mappedHandler.applyPreHandle(processedRequest, response)
// 实际调用handler,重点
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 设置viewName,请求跳转view展示的,现在都是前后端分离的,忽略
applyDefaultViewName(processedRequest, mv);
// 遍历调用拦截的postHandle方法,调用顺序很调用preHandle的时候相反
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 有异常,则这里异常处理器进行处理
// 没异常,则渲染试图(前后端分离忽略),
// 遍历调用拦截器的afterCompletion,调用顺序很调用preHandle的时候相反
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
小总结:HandlerExecutionChain保存了很多拦截器和实际调用的handler,dispatchServlet中初始化了很多handleMapping,所有的handlerMapping都映射不了这个请求,则报错,否则封装成HandlerExecutionChain,再获取HandlerAdapter,然后调用拦截器的prehandler方法,之后调用handler,之后调用拦截器的posthandler方法,如果有错误,则使用异常处理器处理,之后调用拦截器的afterCompletion方法
顺序是:调用拦截器prehandler-》调用handler-》调用拦截器postHandler-》可能调用异常处理器-》调用拦截器afterCompletion
重点关注AbstractHandlerMethodAdapter调用handler的方法,
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
关键看这里:
// 这里获取参数,根据http流,找一个可用的参数解析器,然后进行解析,如果是@requestBody修饰的,则用对应的参数解析器RequestResponseBodyMethodProcessor处理,然后它会找各个HttpMessageConvertor转换,如果引入了fastjsoncovertor,则几乎都是用这个转换
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 这里获取参数,根据http流,遍历所有的方法参数,一个一个找一个可用的参数解析器,然后进行解析,如果是
//@requestBody修饰的,则用对应的参数解析器
//RequestResponseBodyMethodProcessor处理,然后它会找各个
//HttpMessageConvertor转换,如果引入了fastjsoncovertor,则几乎都是用这个转换
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
// 有参数了,则调用我们的controller类
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
遍历所有的方法参数,一个一个找对应的参数解析器
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
// 遍历
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 通过参数解析器的supportsParameter方法判断是否支持解析
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
// 支持,则解析
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " +
parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
}
}
return args;
}
比如,经常会进入这个复合参数解析器:HandlerMethodArgumentResolverComposite
复合参数解析器,里面包含了很多解析器,会一个一个遍历,找是否支持这个参数解析
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (getArgumentResolver(parameter) != null);
}
// 复合参数解析器,里面包含了很多解析器,会一个一个遍历,找是否支持这个参数解析
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
比如如果是@requestBody修饰的参数,复合参数解析器,会找来RequestResponseBodyMethodProcessor来对它解析
// RequestResponseBodyMethodProcessor解析器,支持参数注解有requestBody的
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
RequestResponseBodyMethodProcessor的解析
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 就是这个方法,进行的解析,会遍历各个HttpMessageConvertor进行解析
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
// 解析之后,进行DataBinder进行数据绑定、数据校验
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
主要看它的HttpMessageConverter,
HttpMessage就好像是一个数据包,转换器则是把请求体和响应体进行转换。
HttpMessageConverter,有canRead,read,分别是判断请求是否可以转换,以及进行转换,canwriter和writer分别是判断响应体是否可以转换,以及转换。
注意,比如fastJsonConverter是读取tcp流,然后转换成对象的,所以流读完了就不能重复读了,所以,controller如果还有其他参数,再一次参数解析器解析的时候,就直接null返回了。
相应分析如下:
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) {
MediaType contentType;
boolean noContentType = false;
contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
// 重点在这里,有很多个HttpMessageConverters
// FastJsonHttpMessageConverter:我们自己添加的转换器,转换json的,比如@requestBody输出的json就是它转换的
// StringHttpMessageConverter: 转换成字符串,比如:@requestBody的入参
// String param,StringHttpMessageConverter会把请求体body中的数据转换成
// string,注意是请求体,get请求url中的参数转不了
// MappingJackson2HttpMessageConverter:这个也是转换json的,效果和fastjson差不多,如果没有引入fastjson,就是它转换的json
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 各自转换器,通过canRead判断是否可转换,read进行转换
// canRead和read是HttpMessageConverter接口的方法,所有转换器都必须实现
// 各自转换器的逻辑canRead一般为根据请求MediaType,class类型判断
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
// 可以看到,转换的是body
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
return body;
}
看一下fastjson转换器的代码。
在媒体类型兼容的情况下,fastjson可以转换任何类型
所以,@requestBody 任何类型,都可以转换。但是再一次强调,一定是请求的body数据。
而且因为fastjson转换器,是后一个加入的,所以,最先使用的转换器就是fastjson,转换不了,才用其他的
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
// 转换的方法,也就是JSON.parseObject
private Object readType(Type type, HttpInputMessage inputMessage) throws IOException {
try {
InputStream in = inputMessage.getBody();
return JSON.parseObject(in, fastJsonConfig.getCharset(), type, fastJsonConfig.getFeatures());
} catch (JSONException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex);
} catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
}
}
read分析完了,writer类似,就不分析了。
总结一下:调用handler的过程,1、会遍历所有的方法参数,然后再遍历找参数解析器,比如会找到复合参数解析器,然后复合参数解析器再遍历找到httprequestbody参数解析器,然后,这个解析器,遍历httpMessageConvertor对参数进行转换,比如FastJsonConvertor则直接对参数进行反系列话,而且,这种转换是读取流的,一个参数进行了参数转换,下一个参数转换就是null了,因为流不能重复读。每个参数转换完之后,会进行DataBinder数据校验等相关工作,最后,之后,才会进行调用我们自己的controller方法。
所以,如果你有个性化的需求,可以添加参数解析器,或者,添加httpMessageConvertor
比如,你如果想多个参数都用requestBody修饰,然后都能注入到值,则可以添加自己的参数解析器,再比如,fastjson就添加了自己的httpMessageConvertor
贴一个例子:
浏览器url访问的时候,会报错,转换不了,因为httpmessage转换的是请求体。
用postman,输入请求体的方式能正常访问。
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("test")
public Perso listProvinceCompany(@RequestBody Perso s) {
return new Perso().setName("hello");
}
}
回过头来说一下,就是数据绑定类。
在httpmessage转换了数据之后,还有一个数据绑定类,里面有很多数据转换器,把字符串转换成整形,把字符串转换成对象等,当然这些对requestBody没有用,因为requestBody转换的时候,已经从流中读取完了。
比如:如果get请求url附带参数,controller中,直接使用bean接受,发挥作用的就是这里的类型转换器。
顺便看一个图:
过滤器自然第一个,servlet第二个,servlet调用拦截器prehandle第三个,调用handler的时候会httpmessage转换第四个,aop是getbean初始化的时候BeanPostProcess做的增强代理,可以看成controller,所以第五个,之后真正controller第六个,aop第七个,消息转换第八个,拦截器第九个。