从Servlet容器到我们编写的handler方法,Spring帮我们做了很多事情。比如根据请求URL查找到目标handler方法,参数的类型转换及绑定,调用handler方法,以及返回值写回响应等等操作。本文就来分析一下这个过程中Spring到底帮我们做了哪些事情,以及是怎么做的。
Spring MVC使用的是单servlet模式,这个servlet就是DispatcherServlet,所有请求都会进入该对象中被处理。那servlet容器是怎么调用到DispatcherServlet的呢?先来搞清楚DipatcherServlet的继承体系。
DispatcherServlet的继承体系
Servlet规范定义当请求到来时,servlet中的service方法会被调用。
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
@Override
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
// 对请求响应对象进行转换
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求的方法类型
String method = req.getMethod();
/*
* 下面根据不同类型的请求做不同的处理
* 在Spring中,这里调用的doXXX方法在FrameworkServlet中都重写了。
*/
if (method.equals(METHOD_GET)) { // 处理GET请求
long lastModified = getLastModified(req);
// 默认实现就是-1,所以种类满足条件
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
// 处理请求
doGet(req, resp);
} else {
}
} else if (method.equals(METHOD_HEAD)) { // 处理HEAD请求
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
// 处理请求
doHead(req, resp);
} else if (method.equals(METHOD_POST)) { // 处理POST请求
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) { // 处理PUT请求
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) { // 处理DELETE请求
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) { // 处理OPTIONS请求
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) { // 处理TRACE请求
doTrace(req,resp);
} else { // 不支持的请求方法类型
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
// 响应错误
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
主要的实现在HttpServlet中,自己定义了重载的service方法,该方法中针对不同的HTTP请求方法,调用了不同函数处理。在Spring Framework中的FrameworkServlet重写了这个重载的service方法,目的是为了提供对PATCH类型请求的支持。
注意这里说的重载service方法,是相对于实现Servlet接口中的那个service方法而言的。
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 对PATCH请求的支持
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
// 调用HttpServlet中的是实现
super.service(request, response);
}
}
可以看到,大部分请求还是通过父类HttpServlet的重载service方法来实现的。但是调用的doXXX方法在FrameworkServlet中都被重写了,所以这里直接看这个子类中的即可。
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// 请求处理,这是个模板方法,由子类实现
doService(request, response);
}
}
// 模板方法,由子类实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
这里只给出了doGet和doPost的实现,对于其他类型请求的doXXX方法是相似的,都是调用的processRequest方法,而在该方法中,调用了子类实现的doService方法,下面就来看一下DispatcherServlet中是怎么实现的。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
RequestPath previousRequestPath = null;
// 这个属性是在initHandlerMappings方法中被设置的,在Spring Boot中默认是true
if (this.parseRequestPath) {
// 获取当前Request对象承载的上一次请求的路径
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
// 解析当前请求的路径
ServletRequestPathUtils.parseAndCache(request);
}
try {
// 处理请求
doDispatch(request, response);
}
}
@SuppressWarnings("deprecation")
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.
// 获取封装了handler和interceptor的执行链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 处理没有找到handler的情况
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 执行拦截器前置逻辑,如果有一个拦截器,返回false,那么后续逻辑不会执行了。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 调用handler方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 应用默认视图名称,这一步可以忽略,因为很多项目都是前后段分离的,不会渲染视图,所以这里不会用到。
applyDefaultViewName(processedRequest, mv);
// 执行拦截器后置逻辑
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 处理执行结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
finally {
else {
// Clean up any resources used by a multipart request.
// 如果是文件上传请求
if (multipartRequestParsed) {
// 清理上传的文件
cleanupMultipart(processedRequest);
}
}
}
}
在doService方法中,忽略掉非主要的实现,主要就是调用的doDispatch方法来处理请求。在doDispatch方法中,包含了Spring MVC的核心实现,下面依次来分析该方法中的执行过程。
处理文件上传
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
else {
try {
// 解析文件
return this.multipartResolver.resolveMultipart(request);
}
}
}
// If not returned before: return original request.
return request;
}
该方法中主要做了两件事情:
- 判断请求中是否上传了文件;
- 处理请求中的文件;
::: 由于不同的MultipartResolver有不同的实现,在Spring Boot中默认配置的是StandardServletMultipartResolver,所以以该类为例进行分析。 :::
isMultipart
private boolean resolveLazily = false;
private boolean strictServletCompliance = false;
@Override
public boolean isMultipart(HttpServletRequest request) {
/*
* 从Content-Type中查找是否以multipart关键字开头。
*/
return StringUtils.startsWithIgnoreCase(request.getContentType(),
// strictServletCompliance默认是false,表示不用严格遵守servlet的规范,所以只要contentType以“multipart/”开头就行了。
(this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}
public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";
可以看到,这里是在判断请求头中的ContentType,默认情况下,只要以"multipart/"开头就会被认为是文件上传请求。
resolveMultipart
private boolean resolveLazily = false;
private boolean strictServletCompliance = false;
@Override
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
/*
* 创建standard multipart请求对象
* 如果不是延迟解析,那么会在其构造器中就执行解析
*/
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
请求的解析在StandardMultipartHttpServletRequest的构造方法中。
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
super(request);
if (!lazyParsing) {
// 解析请求
parseRequest(request);
}
}
private void parseRequest(HttpServletRequest request) {
try {
// getParts由具体的servlet容器实现
Collection<Part> parts = request.getParts();
// 保存参数名称
this.multipartParameterNames = new LinkedHashSet<>(parts.size());
// 该集合保存了上传的多个文件
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
// 遍历多个文件并处理
for (Part part : parts) {
// 获取Content-Disposition这个请求头字段
String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
// 解析Content-Disposition这个请求头
ContentDisposition disposition = ContentDisposition.parse(headerValue);
// 获取文件名称
String filename = disposition.getFilename();
if (filename != null) { // 如果是文件
// 对RFC 2047 (MIME encoding for non-ASCII text in headers)的支持
if (filename.startsWith("=?") && filename.endsWith("?=")) {
// 对文件名称进行解码
filename = MimeDelegate.decode(filename);
}
// 创建MultipartFile对象,文件数据在part对象中
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
else { // 如果是普通参数
this.multipartParameterNames.add(part.getName());
}
}
// 将文件对象列表放入请求中
setMultipartFiles(files);
}
catch (Throwable ex) {
handleParseFailure(ex);
}
}
这里是在解析请求中的参数,将文件和普通参数分开存储。这里的文件对象类型是StandardMultipartFile,该对象最终会被传递给handler方法的MultipartFile参数。
获取handler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历列表中的多个handlerMapping,越前面的优先级越高
for (HandlerMapping mapping : this.handlerMappings) {
// 获取执行链,主要都是通过RequestMappingHandlerMapping来获取的
HandlerExecutionChain handler = mapping.getHandler(request);
// 如果找到了,就返回
if (handler != null) {
return handler;
}
}
}
return null;
}
这里以RequestMappingHandlerMapping为例进行分析,主要的实现在AbstractHandlerMethodMapping中。
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 调用子类的实现,具体参考RequestMappingHandlerMapping
Object handler = getHandlerInternal(request);
if (handler == null) {
// 获取默认的handler
handler = getDefaultHandler();
}
// 如果没找到handler
if (handler == null) {
// 则返回null,让调用方从其他的HandlerMapping中查找看看能不能找到合适的handler
return null;
}
// Bean name or resolved handler?
// 如果handler是个字符串
if (handler instanceof String) {
String handlerName = (String) handler;
// 将handler的名称作为bean的名称从bean工厂中获取bean
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
// 如果查询缓存还没有记录,
if (!ServletRequestPathUtils.hasCachedPath(request)) {
// 则初始化缓存项
initLookupPath(request);
}
// 获取执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 跨域相关
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
该方法主要做了两件事情:
- 获取handler方法;
- 将handler方法封装为执行链;
获取handler方法
AbstractHandlerMapping中的getHandlerInternal方法是个模板方法,子类RequestMappingInfoHandlerMapping中实现了,但是主要是调用父类AbstractHanlderMethodMapping中的实现。这几个类的的关系文字叙述起来很绕,直接看下面的类图。
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
// 获取handler
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取查找路径
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
// 查找handler方法
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
主要来参考AbstractHandlerMethodMapping中的实现。
获取查找路径
protected String initLookupPath(HttpServletRequest request) {
// 是否使用路径模式,在Spring Boot中默认情况下满足条件
if (usesPathPatterns()) {
request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
// 解析请求路径
RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
// 获取请求路径的值
String lookupPath = requestPath.pathWithinApplication().value();
// 移除路径中的分号
return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
}
else {
// 解析并缓存查找路径
return getUrlPathHelper().resolveAndCacheLookupPath(request);
}
}
在ServletRequestPathUtils的getParsedRequestPath方法中还是有些许复杂的,但现在只需要直到这里获取到了请求路径就行了。
查找handler方法
下面来看这个获取handler操作的核心,是如何查找handler方法。
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根据路径查找RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
// 如果能直接匹配上
if (directPathMatches != null) {
// 将结果添加到matches列表中
addMatchingMappings(directPathMatches, matches, request);
}
// 如果没有匹配上的handler
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
// 如果存在匹配上了的handler
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 如果有多个匹配
if (matches.size() > 1) {
// 创建比较器
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 排序
matches.sort(comparator);
// 默认获取第一个作为最佳匹配
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// 是否是预检请求
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
// 返回特殊的HandlerMethod
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
// 获取次最佳匹配
Match secondBestMatch = matches.get(1);
// 比较最佳和次最佳的两个handler方法是否是同等级别的,虽然上面排了序,但是可能两个同等级别的排在最前面
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 + "}");
}
}
}
// 把最佳handler添加到请求属性中
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
// 获取handler方法
return bestMatch.getHandlerMethod();
}
else {
// 没有匹配上
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
封装执行链
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler :
// 创建执行链对象
new HandlerExecutionChain(handler));
// 遍历拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
// 是否能拦截该请求
if (mappedInterceptor.matches(request)) {
// 如果匹配请求则添加到执行链中
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
这里遍历的拦截器是什么时候设置的呢?AbstractHandlerMapping继承了ApplicationObjectSupport,而后者又实现了ApplicationContextAware接口并实现了setApplicationContext方法。在该方法中最终会调用到AbstractHandlerMapping中的initApplicationContext方法。
@Override
protected void initApplicationContext() throws BeansException {
// 扩展拦截器,默认是空实现
extendInterceptors(this.interceptors);
// 获取拦截器
detectMappedInterceptors(this.adaptedInterceptors);
// 初始化拦截器
initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
// 获取bean工厂中所有MappedInterceptor类型的bean
mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
// 遍历拦截器对象
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
// 确保列表中的拦截器对象不能是null
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
// 将拦截器转换为HandlerInterceptor类型的对象
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
获取handler适配器
上面获取到handler后,就是根据handler来获取handler适配器。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历适配器列表
for (HandlerAdapter adapter : this.handlerAdapters) {
// 判断适配器是否支持该handler
if (adapter.supports(handler)) {
return adapter;
}
}
}
// 如果没有支持当前handler的适配器,则抛出异常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
// 支持所有请求
return true;
}
对于常用的HandlerMethod而言,使用的是RequestMappingHandlerAdapter这个适配器,所以本文而以该类为例介绍。
调用拦截器前置逻辑
接下来是执行拦截器的前置逻辑。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 遍历拦截器列表
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
// 调用拦截器的前置逻辑,即preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
如果有一个拦截器,返回false,那么后续逻辑不会执行了。
执行handler方法
接下来是最重要的步骤,执行handler方法,即真正的处理请求。
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
核心实现在子类RequestMappingHandlerAdapter中。
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
// 调用handler方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 如果响应头中不包含Cache-Control
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
上面方法中的几种情况都会调用到invokeHandlerMethod方法。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response); // 封装请求响应对象
try {
// 获取数据绑定工厂对象
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取模型工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 真正对handlerMethod的invoke操作其实是委托给invocableMethod来实现的,所以invocableMethod是关键核心
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
/*
* 设置参数解析器
* argumentResolvers是在afterPropertiesSet方法中被调用的。
* 下面设置的returnValueHandlers也一样。
*/
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 设置binder工厂
invocableMethod.setDataBinderFactory(binderFactory);
// 设置参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 创建模型视图容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 异步支持
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 调用方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 内部会模型视图对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
创建可执行的handler方法
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
这一步很简单,将handler方法封装到ServletInvocableHandlerMethod对象中,以便后续调用。 然后为ServletInvocableHandlerMethod对象设置了参数和返回值解析器,分别用于后续的参数解析和返回值处理。
执行handler方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 调用方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 处理@ResponseStatus注解
setResponseStatus(webRequest);
// 如果没有返回值
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
// 丢弃缓存
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
// 如果存在@ResponseStatus注解,且reason字段不是空字符串
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
// 执行到这里是需要返回值处理器处理的情况,所以returnValueHandlers不能是null。
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 通过返回值处理器来处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
方法真正的执行是在父类InvocableHandlerMethod中的invokeForRequest方法中。
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取方法参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 执行方法
return doInvoke(args);
}
上面两个方法的逻辑可以分为下面三步:
- 获取参数;
- 执行方法;
- 处理返回值;
获取参数
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters(); // 获取方法参数,将每个参数封装成MethodParameter对象
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);
// 判断是否提供了该参数,在RequestMappingHandlerAdapter的invokeHandlerMethod方法中调用过来时,没有传递的providedArgs参数
args[i] = findProvidedArgument(parameter, providedArgs);
// 对于大多数正常情况下,args[i]肯定是null。因为大部分都是从请求中获取参数。
if (args[i] != null) {
continue;
}
/*
* 判断参数解析器集合中是否存在能够解析该参数的解析器,
* 如果没有一个解析器支持解析该参数,那么抛出异常
*/
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 从请求中解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
}
return args;
}
@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
if (!ObjectUtils.isEmpty(providedArgs)) {
// 遍历提供的实际参数
for (Object providedArg : providedArgs) {
// 如果实参类型能够满足形参类型
if (parameter.getParameterType().isInstance(providedArg)) {
// 则返回参数值
return providedArg;
}
}
}
return null;
}
这里会先判断是否有支持该参数的参数解析器,如果没有则会抛出异常
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@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);
break;
}
}
}
return result;
}
然后通过参数解析器来从请求中或者模型视图容器中解析参数。
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
/*
* 根据参数寻找合适的解析器
* 这里需要去看一下Spring提供的默认的参数解析器都能够解析哪些参数,也就是去看各种HandlerMethodArgumentResolver的supportsParameter方法
*/
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);
}
由于Spring中的参数解析器太多,这里就不一一例举了,后面单独写文章来分析一些常用的参数解析器。
执行方法
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
try {
// 通过反射调用我们编写的handler方法
return method.invoke(getBean(), args);
}
}
这里通过反射调用我们编写的handler方法,至此请求入站的Spring框架部分分析完毕,下面属于出站部分。
处理返回值
在ServletInvocableHandlerMethod的invokeAndHandle方法中,先是处理了@ResponseStatus注解,这个用得比较少,省略介绍;主要是通过返回值处理器来处理。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 根据返回值选择处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
// 没有处理器能够处理返回值
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 通过具体的处理器来处理返回值
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判断是否是异步返回值
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
// 如果处理值是处理异步的情况,则跳过
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 判断处理器是否支持这种返回值类型
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
由于Spring中的返回值处理器太多,这里就不一一例举了,后面单独写文章来分析一些常用的返回值处理器。
关于ModelAndView,现如今大量项目都是前后段分离项目,后端只提供JSON响应,不渲染视图。所以本文不会分析ModelAndView。 实际上,大量项目的控制器类都被@ResponseBody或者@RestController注解修饰,返回值会被RequestResponseBodyMethodProcessor处理,该类会把ModelAndViewContainer中的requestHandled属性标记为true,所以后续回到RequestMappingHandlerAdapter的getModelAndView方法中时,是不会创建ModelAndView对象的。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置设置为true,后续不会创建ModelAndView对象
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 将JSON数据写入响应体中
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 更新模型
modelFactory.updateModel(webRequest, mavContainer);
// 如果请求已经处理过了,则返回null
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
// 创建mv对象
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
调用拦截器后置逻辑
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
// 遍历拦截器
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
// 执行拦截器的后置逻辑,即postHandle方法
interceptor.postHandle(request, response, this.handler, mv);
}
}
文件清理
protected void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null) {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
if (multipartRequest != null) {
// 清理文件
this.multipartResolver.cleanupMultipart(multipartRequest);
}
}
}
调用具体的MultipartResolver来实现,本文以StandardServletMultipartResolver为例。
@Override
public void cleanupMultipart(MultipartHttpServletRequest request) {
if (!(request instanceof AbstractMultipartHttpServletRequest) ||
((AbstractMultipartHttpServletRequest) request).isResolved()) {
// To be on the safe side: explicitly delete the parts,
// but only actual file parts (for Resin compatibility)
try {
// 遍历每个文件
for (Part part : request.getParts()) {
if (request.getFile(part.getName()) != null) {
/*
* 这里要看具体servlet容器的实现
*/
part.delete();
}
}
}
catch (Throwable ex) {
LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
}
}
}
注意,Spring在请求处理末尾会删除上传的文件,如果在业务中需要异步处理文件的,首先需要同步拷贝文件,否则其他线程读文件时,该文件已经被删除了。而且这种问题的偶发性的,有时出现有时不出现,原因就在于线程调度,取决于读线程是否在删除文件之前就读到内容。
总结
本文梳理了Spring MVC请求的处理过程,大致可以总结为下面5个步骤:
- 获取执行链,包括handler方法和拦截器;
- 获取handler适配器;
- 执行拦截器的前置逻辑;
- 执行handler方法;
- 执行拦截器的后置逻辑;
如果要加上视图的话,还应有视图解析和视图渲染。